src/require.js
/**
* @file Require implementation
* @since 0.2.2
*/
/*#ifndef(UMD)*/
"use strict";
/*global _gpfArrayForEach*/ // Almost like [].forEach (undefined are also enumerated)
/*global _gpfPathJoin*/ // Join all arguments together and normalize the resulting path
/*global _gpfRequireConfigure*/ // Configure the {@link gpf.require} layer
/*global _gpfRequireLoad*/ // Load the resource
/*exported _gpfRequireAllocate*/ // Allocate a new require context with the proper methods
/*#endif*/
/* this is globally used as the current context in this module */
/*jshint -W040*/
/*eslint-disable no-invalid-this*/
/**
* @typedef gpf.typedef._requireContext
* @property {String} base Base path used to resolve names
* @property {Object} cache Dictionary of loaded requires
* @property {Object} preload Dictionary of preloaded requires
* @property {gpf.typedef.requirePreprocessFunc} preprocess Preprocess function
* @since 0.2.2
*/
/**
* Resolves the resource name according to current require context.
*
* @param {String} name Relative resource name
* @return {String} Resolved name
* @since 0.2.2
*
*/
function _gpfRequireResolve (name) {
return _gpfPathJoin(this.base, name);
}
function _gpfRequireDocumentStack (reason, name) {
if (!Array.isArray(reason.requires)) {
reason.requires = [];
}
reason.requires.push(name);
}
/**
* Get the cached resource or load it
*
* @param {String} name Resource name
* @return {Promise<*>} Resource association
* @since 0.2.2
*/
function _gpfRequireGet (name) {
var me = this,
promise;
if (me.cache[name]) {
return me.cache[name];
}
promise = _gpfRequireLoad.call(me, name);
me.cache[name] = promise;
return promise["catch"](function (reason) {
_gpfRequireDocumentStack(reason, name);
return Promise.reject(reason);
});
}
/**
* Defines a new module by executing the factory function with the specified dependent resources,
* see {@tutorial REQUIRE}
*
*
* @param {Object} dependencies Dictionary of dependencies, the keys are preserved while passing the result
* dictionary to the factory function
* @param {*} factory Can be either:
* * A factory function executed when all resources are resolved, the first parameter will be a dictionary
* with all dependencies indexed by their name (as initially specified in the dependencies parameter).
* The result of the factory function will be cached as the result of this resource
* * Any value that will be cached as the result of this resource
* @return {Promise<*>} Resolved with the factory function result or the object
* @since 0.2.2
*/
function _gpfRequireDefine (dependencies, factory) {
var me = this,
promises = [],
keys = Object.keys(dependencies);
_gpfArrayForEach(keys, function (key) {
promises.push(_gpfRequireGet.call(me, _gpfRequireResolve.call(me, dependencies[key])));
}, me);
return Promise.all(promises)
.then(function (resources) {
var result,
require;
if (typeof factory === "function") {
require = {};
_gpfArrayForEach(keys, function (key, index) {
require[key] = resources[index];
});
result = factory(require);
} else {
result = factory;
}
return result;
});
}
/**
* Allocate a new require context with the proper methods
*
* @param {Object} parentContext Context to inherit from
* @param {gpf.typedef.requireOptions} [options] Options to configure
* @return {Object} Containing {@link gpf.require.define}, {@link gpf.require.resolve} and {@link gpf.require.configure}
* @since 0.2.2
*/
function _gpfRequireAllocate (parentContext, options) {
var context = Object.create(parentContext), // cache content is shared but other properties are protected
require = {};
require.define = _gpfRequireDefine.bind(context);
require.resolve = _gpfRequireResolve.bind(context);
require.configure = _gpfRequireConfigure.bind(context);
if (options) {
require.configure(options);
}
return require;
}
gpf.require = _gpfRequireAllocate({
base: "",
cache: {},
preload: {},
preprocess: function (resource) {
return Promise.resolve(resource);
}
});
/**
* @method gpf.require.define
* @gpf:sameas _gpfRequireDefine
* @since 0.2.2
*/
/**
* @method gpf.require.resolve
* @gpf:sameas _gpfRequireResolve
* @since 0.2.2
*
* @example <caption>Setting the base path</caption>
* gpf.require.configure({
* base: "/test/require"
* });
* assert(gpf.require.resolve("file.js") === "/test/require/file.js");
*/