301 lines
9.7 KiB
JavaScript
301 lines
9.7 KiB
JavaScript
/** vim: et:ts=4:sw=4:sts=4
|
|
* @license amdefine 1.0.0 Copyright (c) 2011-2015, The Dojo Foundation All Rights Reserved.
|
|
* Available via the MIT or new BSD license.
|
|
* see: http://github.com/jrburke/amdefine for details
|
|
*/
|
|
|
|
/*jslint node: true */
|
|
/*global module, process */
|
|
'use strict';
|
|
|
|
/**
|
|
* Creates a define for node.
|
|
* @param {Object} module the "module" object that is defined by Node for the
|
|
* current module.
|
|
* @param {Function} [requireFn]. Node's require function for the current module.
|
|
* It only needs to be passed in Node versions before 0.5, when module.require
|
|
* did not exist.
|
|
* @returns {Function} a define function that is usable for the current node
|
|
* module.
|
|
*/
|
|
function amdefine(module, requireFn) {
|
|
'use strict';
|
|
var defineCache = {},
|
|
loaderCache = {},
|
|
alreadyCalled = false,
|
|
path = require('path'),
|
|
makeRequire, stringRequire;
|
|
|
|
/**
|
|
* Trims the . and .. from an array of path segments.
|
|
* It will keep a leading path segment if a .. will become
|
|
* the first path segment, to help with module name lookups,
|
|
* which act like paths, but can be remapped. But the end result,
|
|
* all paths that use this function should look normalized.
|
|
* NOTE: this method MODIFIES the input array.
|
|
* @param {Array} ary the array of path segments.
|
|
*/
|
|
function trimDots(ary) {
|
|
var i, part;
|
|
for (i = 0; ary[i]; i+= 1) {
|
|
part = ary[i];
|
|
if (part === '.') {
|
|
ary.splice(i, 1);
|
|
i -= 1;
|
|
} else if (part === '..') {
|
|
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
|
|
//End of the line. Keep at least one non-dot
|
|
//path segment at the front so it can be mapped
|
|
//correctly to disk. Otherwise, there is likely
|
|
//no path mapping for a path starting with '..'.
|
|
//This can still fail, but catches the most reasonable
|
|
//uses of ..
|
|
break;
|
|
} else if (i > 0) {
|
|
ary.splice(i - 1, 2);
|
|
i -= 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function normalize(name, baseName) {
|
|
var baseParts;
|
|
|
|
//Adjust any relative paths.
|
|
if (name && name.charAt(0) === '.') {
|
|
//If have a base name, try to normalize against it,
|
|
//otherwise, assume it is a top-level require that will
|
|
//be relative to baseUrl in the end.
|
|
if (baseName) {
|
|
baseParts = baseName.split('/');
|
|
baseParts = baseParts.slice(0, baseParts.length - 1);
|
|
baseParts = baseParts.concat(name.split('/'));
|
|
trimDots(baseParts);
|
|
name = baseParts.join('/');
|
|
}
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Create the normalize() function passed to a loader plugin's
|
|
* normalize method.
|
|
*/
|
|
function makeNormalize(relName) {
|
|
return function (name) {
|
|
return normalize(name, relName);
|
|
};
|
|
}
|
|
|
|
function makeLoad(id) {
|
|
function load(value) {
|
|
loaderCache[id] = value;
|
|
}
|
|
|
|
load.fromText = function (id, text) {
|
|
//This one is difficult because the text can/probably uses
|
|
//define, and any relative paths and requires should be relative
|
|
//to that id was it would be found on disk. But this would require
|
|
//bootstrapping a module/require fairly deeply from node core.
|
|
//Not sure how best to go about that yet.
|
|
throw new Error('amdefine does not implement load.fromText');
|
|
};
|
|
|
|
return load;
|
|
}
|
|
|
|
makeRequire = function (systemRequire, exports, module, relId) {
|
|
function amdRequire(deps, callback) {
|
|
if (typeof deps === 'string') {
|
|
//Synchronous, single module require('')
|
|
return stringRequire(systemRequire, exports, module, deps, relId);
|
|
} else {
|
|
//Array of dependencies with a callback.
|
|
|
|
//Convert the dependencies to modules.
|
|
deps = deps.map(function (depName) {
|
|
return stringRequire(systemRequire, exports, module, depName, relId);
|
|
});
|
|
|
|
//Wait for next tick to call back the require call.
|
|
if (callback) {
|
|
process.nextTick(function () {
|
|
callback.apply(null, deps);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
amdRequire.toUrl = function (filePath) {
|
|
if (filePath.indexOf('.') === 0) {
|
|
return normalize(filePath, path.dirname(module.filename));
|
|
} else {
|
|
return filePath;
|
|
}
|
|
};
|
|
|
|
return amdRequire;
|
|
};
|
|
|
|
//Favor explicit value, passed in if the module wants to support Node 0.4.
|
|
requireFn = requireFn || function req() {
|
|
return module.require.apply(module, arguments);
|
|
};
|
|
|
|
function runFactory(id, deps, factory) {
|
|
var r, e, m, result;
|
|
|
|
if (id) {
|
|
e = loaderCache[id] = {};
|
|
m = {
|
|
id: id,
|
|
uri: __filename,
|
|
exports: e
|
|
};
|
|
r = makeRequire(requireFn, e, m, id);
|
|
} else {
|
|
//Only support one define call per file
|
|
if (alreadyCalled) {
|
|
throw new Error('amdefine with no module ID cannot be called more than once per file.');
|
|
}
|
|
alreadyCalled = true;
|
|
|
|
//Use the real variables from node
|
|
//Use module.exports for exports, since
|
|
//the exports in here is amdefine exports.
|
|
e = module.exports;
|
|
m = module;
|
|
r = makeRequire(requireFn, e, m, module.id);
|
|
}
|
|
|
|
//If there are dependencies, they are strings, so need
|
|
//to convert them to dependency values.
|
|
if (deps) {
|
|
deps = deps.map(function (depName) {
|
|
return r(depName);
|
|
});
|
|
}
|
|
|
|
//Call the factory with the right dependencies.
|
|
if (typeof factory === 'function') {
|
|
result = factory.apply(m.exports, deps);
|
|
} else {
|
|
result = factory;
|
|
}
|
|
|
|
if (result !== undefined) {
|
|
m.exports = result;
|
|
if (id) {
|
|
loaderCache[id] = m.exports;
|
|
}
|
|
}
|
|
}
|
|
|
|
stringRequire = function (systemRequire, exports, module, id, relId) {
|
|
//Split the ID by a ! so that
|
|
var index = id.indexOf('!'),
|
|
originalId = id,
|
|
prefix, plugin;
|
|
|
|
if (index === -1) {
|
|
id = normalize(id, relId);
|
|
|
|
//Straight module lookup. If it is one of the special dependencies,
|
|
//deal with it, otherwise, delegate to node.
|
|
if (id === 'require') {
|
|
return makeRequire(systemRequire, exports, module, relId);
|
|
} else if (id === 'exports') {
|
|
return exports;
|
|
} else if (id === 'module') {
|
|
return module;
|
|
} else if (loaderCache.hasOwnProperty(id)) {
|
|
return loaderCache[id];
|
|
} else if (defineCache[id]) {
|
|
runFactory.apply(null, defineCache[id]);
|
|
return loaderCache[id];
|
|
} else {
|
|
if(systemRequire) {
|
|
return systemRequire(originalId);
|
|
} else {
|
|
throw new Error('No module with ID: ' + id);
|
|
}
|
|
}
|
|
} else {
|
|
//There is a plugin in play.
|
|
prefix = id.substring(0, index);
|
|
id = id.substring(index + 1, id.length);
|
|
|
|
plugin = stringRequire(systemRequire, exports, module, prefix, relId);
|
|
|
|
if (plugin.normalize) {
|
|
id = plugin.normalize(id, makeNormalize(relId));
|
|
} else {
|
|
//Normalize the ID normally.
|
|
id = normalize(id, relId);
|
|
}
|
|
|
|
if (loaderCache[id]) {
|
|
return loaderCache[id];
|
|
} else {
|
|
plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
|
|
|
|
return loaderCache[id];
|
|
}
|
|
}
|
|
};
|
|
|
|
//Create a define function specific to the module asking for amdefine.
|
|
function define(id, deps, factory) {
|
|
if (Array.isArray(id)) {
|
|
factory = deps;
|
|
deps = id;
|
|
id = undefined;
|
|
} else if (typeof id !== 'string') {
|
|
factory = id;
|
|
id = deps = undefined;
|
|
}
|
|
|
|
if (deps && !Array.isArray(deps)) {
|
|
factory = deps;
|
|
deps = undefined;
|
|
}
|
|
|
|
if (!deps) {
|
|
deps = ['require', 'exports', 'module'];
|
|
}
|
|
|
|
//Set up properties for this module. If an ID, then use
|
|
//internal cache. If no ID, then use the external variables
|
|
//for this node module.
|
|
if (id) {
|
|
//Put the module in deep freeze until there is a
|
|
//require call for it.
|
|
defineCache[id] = [id, deps, factory];
|
|
} else {
|
|
runFactory(id, deps, factory);
|
|
}
|
|
}
|
|
|
|
//define.require, which has access to all the values in the
|
|
//cache. Useful for AMD modules that all have IDs in the file,
|
|
//but need to finally export a value to node based on one of those
|
|
//IDs.
|
|
define.require = function (id) {
|
|
if (loaderCache[id]) {
|
|
return loaderCache[id];
|
|
}
|
|
|
|
if (defineCache[id]) {
|
|
runFactory.apply(null, defineCache[id]);
|
|
return loaderCache[id];
|
|
}
|
|
};
|
|
|
|
define.amd = {};
|
|
|
|
return define;
|
|
}
|
|
|
|
module.exports = amdefine;
|