Modularization

The library offers a way to organize any project by creating separate resources files and reference them like dependencies. The {@see gpf.require.define} API is the main entry point.

A resource file is referenced using its path. A resource may reference another resource using a path that is relative to the current one. For the initial call, a base path may be defined using {@see gpf.require.configure}. The resource resolved path correspond to the resource absolute path.

The library handles several type of resources files. Type detection is based on the path extension (case insensitive):

  • .json JSON static resource file
  • .js JavaScript module
  • by default, any other type is handled as a text file

Once a resource file is loaded, its associated output (see below) is cached and indexed using the resolved file path. Consequently (considering the cache is never cleared):

  • If the same resource (i.e. the same resolved path) is accessed twice, the file will be loaded only once
  • Any modification applied to the output will be reflected globally

JSON static resource file

A JSON file is loaded, parsed and the result value is the output.

JavaScript Module

Modules are designed to enforce best practices:

  • The module scope is private, public exposure has to be explicit. This reduces the global namespace cluttering. (see the different supported formats below)
  • Dependencies are loaded first and then made available to the module through injection

Whatever the chosen module format, the top level call must be {@see gpf.require.define}. The factory function is optional and a promised is returned to know when the dependant resources are loaded.

The library supports several module formats. In general, the gpf symbol is always available. If you need an access to the global context, use the {@see gpf.context} API.

GPF Module

This module format is similar to the AMD one but it limits the number of parameters inside the factory function. Indeed, dependencies are explicitly named in a dictionary and consolidated with the same name in one parameter.

The module public API (i.e. resource output) is the result of the factory function.

gpf.require.define({
    name1: "dependency1.js",
    name2: "dependency2.js",
    // ...
    nameN: "dependencyN.js"

}, function (require) {
    "use strict";
    // Private scope

    require.name1.api1();

    // Implementation

    // Interface
    return {

        api1: function (/*...*/) {
            /*...*/
        },

        // ...

        apiN: function (/*...*/) {
            /*...*/
        }

    };

});

Asynchronous Module Definition (AMD)

The library supports the AMD format.

define("amd", [
    "dependency1.js",
    "dependency2.js",
    /*...*/,
    "dependencyN.js"
], function (name1, name2, /*...*/ nameN) {
    "use strict";
    // Private scope

    name1.api1();

    // Implementation

    // Interface
    return {

        api1: function (/*...*/) {
            /*...*/
        },

        // ...

        apiN: function (/*...*/) {
            /*...*/
        }

    };

});

Or a shorter version (no name)

define([
    "dependency1.js",
    "dependency2.js",
    /*...*/,
    "dependencyN.js"
], function (name1, name2, /*...*/ nameN) {
    "use strict";
    // Private scope

    name1.api1();

    // Implementation

    // Interface
    return {

        api1: function (/*...*/) {
            /*...*/
        },

        // ...

        apiN: function (/*...*/) {
            /*...*/
        }

    };

});

Even shorter if you don't need dependencies:

define(function () {
    "use strict";
    // Private scope

    // Implementation

    // Interface
    return {

        api1: function (/*...*/) {
            /*...*/
        },

        // ...

        apiN: function (/*...*/) {
            /*...*/
        }

    };

});

CommonJS

The library also supports the CommonJS format with restrictions:

  • Only static requires can be used (i.e. the require parameter must be a string)
  • Required modules are evaluated even if the require instruction is not evaluated (i.e. inside a condition)

The module public API (i.e. resource output) is the result of the module.export.

"use strict";
// Private scope

var name1 = require("dependency1.js"),
    name2 = require("dependency2.js"),
    // ...
    nameN = require("dependencyN.js");

name1.api1();

// Implementation

// Interface
module.exports = {

    api1: function (/*...*/) {
        /*...*/
    },

    // ...

    apiN: function (/*...*/) {
        /*...*/
    }

};

Preloading

Since version 0.2.7, the library offers a way to inject a dictionary associating resource names to their textual source. The goal is to speed up the application loading by consolidating all dependent resources into a single JSON file and preload them in one call.

// Proposed implementation
gpf.http.get("preload.json")
    .then(function (response) {
        if (response.status === 200) {
            return JSON.parse(response.responseText);
        }
        return Promise.reject();
    })
    .then(function (preload) {
        gpf.require.configure({
            preload: preload
        });
    })
    .catch(function (reason) {
        // Document and/or absorb errors
    })
    .then(function () {
        gpf.require.define({
            app: "app.js" // Might be preloaded
        }, function (require) {
            require.app.start();
        });
    });