Last active
May 25, 2018 13:01
-
-
Save james-jlo-long/f463c43a1fe5058520fe939d52b75942 to your computer and use it in GitHub Desktop.
A poor-man's require. A simple JavaScript module manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var myNamespace = (function () { | |
"use strict"; | |
/** | |
* A collection of all defined modules. | |
* @private | |
* @type {Object} | |
*/ | |
var defined = Object.create(null); | |
var empty; // Created once Definition is created. | |
/** | |
* Gets the resolved dependency. | |
* | |
* @private | |
* @param {String} name | |
* Name of the dependency to get. | |
* @return {Promise} | |
* Promise that resolves with the dependency value. | |
*/ | |
function getResolved(name) { | |
var def = defined[name] || empty; | |
return def.resolve(); | |
} | |
/** | |
* A module definition. | |
* | |
* @constructor | |
* @private | |
* @param {Array.<String>} dependencies | |
* Name of the dependencies for this module. | |
* @param {Function} factory | |
* Function that creates the module. | |
*/ | |
function Definition(dependencies, factory) { | |
/** | |
* The names of the dependencies for this module. | |
* @type {Array.<String>} | |
*/ | |
this.dependencies = dependencies; | |
/** | |
* The factory for creating this module. | |
* @type {Function} | |
*/ | |
this.factory = factory; | |
} | |
Definition.prototype = { | |
/** | |
* Creates the promise that resolves with the return value of | |
* {@link Definition#factory}. All {@link Definition} instances that | |
* match the names in {@link Definition#dependencies} are passed to the | |
* factory in the order in which they were requested. | |
* | |
* @return {Promise} | |
* Promise that resolves with the module value. | |
*/ | |
resolve: function () { | |
var that = this; | |
if (!that.promise) { | |
/** | |
* Promise that resolves with the module definition. | |
* | |
* @type {Promise} | |
*/ | |
that.promise = Promise | |
.all(that.resolveDependencies()) | |
.then(function (values) { | |
return that.factory.apply(undefined, values); | |
}); | |
} | |
return that.promise; | |
}, | |
/** | |
* Converts {@link Definition#dependencies} into resolved module | |
* promises. | |
* | |
* @return {Array.<Promise>} | |
* Resolved module promises. | |
*/ | |
resolveDependencies: function () { | |
return this.dependencies.map(getResolved); | |
} | |
}; | |
/** | |
* A definition that resolves to nothing. Used as a placeholder for any | |
* module that isn't found. | |
* | |
* @private | |
* @type {Definition} | |
*/ | |
empty = new Definition([], function () { | |
return; | |
}); | |
return { | |
/** | |
* Create a module definition. | |
* | |
* @memberof myNamespace | |
* @param {String} name | |
* Name of the module to create. | |
* @param {Array.<String>} [dependencies] | |
* Optional dependencies for the module. | |
* @param {Function} factory | |
* Factory that creates the module. | |
*/ | |
define: function (name, dependencies, factory) { | |
if (factory === undefined) { | |
factory = dependencies; | |
dependencies = []; | |
} | |
defined[name] = new Definition(dependencies, factory); | |
}, | |
/** | |
* Gets a Promise that resolves the with the module's value. If a | |
* handler is passed in, it is executed when the module is resolved but | |
* it does not modify the module's value. | |
* | |
* @memberof myNamespace | |
* @param {Array.<String>} names | |
* Name of the module to retrieve. | |
* @param {Function} [handler] | |
* Optional handler to execute when the module resolves. | |
* @return {Promise} | |
* Promise that resolves with the module's value. | |
*/ | |
require: function (names, handler) { | |
var modules = Promise.all(names.map(getResolved)); | |
if (typeof handler === "function") { | |
modules.then(function (dependencies) { | |
handler.apply(undefined, dependencies); | |
}); | |
} | |
return modules; | |
}, | |
/** | |
* Gets an array of all the defined module names. Pass them to | |
* {@link myNamespace.require} to get the module value(s). | |
* | |
* @memberof myNamespace | |
* @return {Array.<String>} | |
* Array of all the modules that have been defined. | |
*/ | |
getDefined: function () { | |
return Object.keys(defined); | |
} | |
}; | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Creating a module | |
// | |
// Here's a simple module that is created. The function isn't executed until | |
// another module tries to use it and it's only executed once. | |
myNamespace.define("one", function () { | |
return 1; | |
}); | |
// Here's a module that requires another module. This doesn't execute the | |
// function, nor does it execute the dependency's function. When the dependency | |
// is resolved, its value will be passed to this factory. | |
myNamespace.define("two", ["one"], function (one) { | |
return 1 + one; | |
}); | |
// Retrieving a module | |
// | |
// Here is a module getting accessed. This executes the factory function for | |
// "two" (which executes the factory function for "one"). | |
myNamespace.require(["two"], function (two) { | |
console.log(two); | |
}); | |
// Logs: 2 | |
// The function returns a Promise, so you can use `.then()` to add more | |
// callbacks. `.then()` gets an array of module values instead of the individual | |
// values like the callback. | |
myNamespace.require(["two"]).then(function (values) { | |
console.log(values); | |
}); | |
// Logs: [2] | |
// The callback function does not modify the module value. | |
myNamespace.require(["two"], function (two) { | |
return two * 2; | |
}).then(function (values) { | |
console.log(values); | |
}); | |
// Logs: [2] | |
// Replacing a module | |
// | |
// This can be done at any time. | |
myNamespace.require(["two"], function (two) { | |
console.log(two); | |
}); | |
// Logs: 2 | |
myNamespace.define("two", ["one"], function (one) { | |
return 2 + one; | |
}); | |
myNamespace.require(["two"], function (two) { | |
console.log(two); | |
}); | |
// Logs: 3 | |
// Undefined modules | |
// | |
// If a module isn't found, `undefined` is passed to the factory. No error is | |
// thrown. | |
myNamespace.require(["does-not-exist"], function (thing) { | |
console.log(thing); | |
}); | |
// Logs: undefined |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment