amdefine.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /** vim: et:ts=4:sw=4:sts=4
  2. * @license amdefine 1.0.1 Copyright (c) 2011-2016, The Dojo Foundation All Rights Reserved.
  3. * Available via the MIT or new BSD license.
  4. * see: http://github.com/jrburke/amdefine for details
  5. */
  6. /*jslint node: true */
  7. /*global module, process */
  8. 'use strict';
  9. /**
  10. * Creates a define for node.
  11. * @param {Object} module the "module" object that is defined by Node for the
  12. * current module.
  13. * @param {Function} [requireFn]. Node's require function for the current module.
  14. * It only needs to be passed in Node versions before 0.5, when module.require
  15. * did not exist.
  16. * @returns {Function} a define function that is usable for the current node
  17. * module.
  18. */
  19. function amdefine(module, requireFn) {
  20. 'use strict';
  21. var defineCache = {},
  22. loaderCache = {},
  23. alreadyCalled = false,
  24. path = require('path'),
  25. makeRequire, stringRequire;
  26. /**
  27. * Trims the . and .. from an array of path segments.
  28. * It will keep a leading path segment if a .. will become
  29. * the first path segment, to help with module name lookups,
  30. * which act like paths, but can be remapped. But the end result,
  31. * all paths that use this function should look normalized.
  32. * NOTE: this method MODIFIES the input array.
  33. * @param {Array} ary the array of path segments.
  34. */
  35. function trimDots(ary) {
  36. var i, part;
  37. for (i = 0; ary[i]; i+= 1) {
  38. part = ary[i];
  39. if (part === '.') {
  40. ary.splice(i, 1);
  41. i -= 1;
  42. } else if (part === '..') {
  43. if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
  44. //End of the line. Keep at least one non-dot
  45. //path segment at the front so it can be mapped
  46. //correctly to disk. Otherwise, there is likely
  47. //no path mapping for a path starting with '..'.
  48. //This can still fail, but catches the most reasonable
  49. //uses of ..
  50. break;
  51. } else if (i > 0) {
  52. ary.splice(i - 1, 2);
  53. i -= 2;
  54. }
  55. }
  56. }
  57. }
  58. function normalize(name, baseName) {
  59. var baseParts;
  60. //Adjust any relative paths.
  61. if (name && name.charAt(0) === '.') {
  62. //If have a base name, try to normalize against it,
  63. //otherwise, assume it is a top-level require that will
  64. //be relative to baseUrl in the end.
  65. if (baseName) {
  66. baseParts = baseName.split('/');
  67. baseParts = baseParts.slice(0, baseParts.length - 1);
  68. baseParts = baseParts.concat(name.split('/'));
  69. trimDots(baseParts);
  70. name = baseParts.join('/');
  71. }
  72. }
  73. return name;
  74. }
  75. /**
  76. * Create the normalize() function passed to a loader plugin's
  77. * normalize method.
  78. */
  79. function makeNormalize(relName) {
  80. return function (name) {
  81. return normalize(name, relName);
  82. };
  83. }
  84. function makeLoad(id) {
  85. function load(value) {
  86. loaderCache[id] = value;
  87. }
  88. load.fromText = function (id, text) {
  89. //This one is difficult because the text can/probably uses
  90. //define, and any relative paths and requires should be relative
  91. //to that id was it would be found on disk. But this would require
  92. //bootstrapping a module/require fairly deeply from node core.
  93. //Not sure how best to go about that yet.
  94. throw new Error('amdefine does not implement load.fromText');
  95. };
  96. return load;
  97. }
  98. makeRequire = function (systemRequire, exports, module, relId) {
  99. function requireInContext(depName) {
  100. return stringRequire(systemRequire, exports, module, depName, relId);
  101. }
  102. function amdRequire(deps, callback) {
  103. if (typeof deps === 'string') {
  104. //Synchronous, single module require('')
  105. return requireInContext(deps);
  106. } else {
  107. //Array of dependencies with a callback.
  108. if (callback) {
  109. //Wait for next tick to call back the require call.
  110. process.nextTick(function () {
  111. //Convert the dependencies to modules.
  112. callback.apply(null, deps.map(requireInContext));
  113. });
  114. } else {
  115. //Require the dependencies' moduies.
  116. deps.forEach(requireInContext);
  117. }
  118. }
  119. }
  120. amdRequire.toUrl = function (filePath) {
  121. if (filePath.indexOf('.') === 0) {
  122. return normalize(filePath, path.dirname(module.filename));
  123. } else {
  124. return filePath;
  125. }
  126. };
  127. return amdRequire;
  128. };
  129. //Favor explicit value, passed in if the module wants to support Node 0.4.
  130. requireFn = requireFn || function req() {
  131. return module.require.apply(module, arguments);
  132. };
  133. function runFactory(id, deps, factory) {
  134. var r, e, m, result;
  135. if (id) {
  136. e = loaderCache[id] = {};
  137. m = {
  138. id: id,
  139. uri: __filename,
  140. exports: e
  141. };
  142. r = makeRequire(requireFn, e, m, id);
  143. } else {
  144. //Only support one define call per file
  145. if (alreadyCalled) {
  146. throw new Error('amdefine with no module ID cannot be called more than once per file.');
  147. }
  148. alreadyCalled = true;
  149. //Use the real variables from node
  150. //Use module.exports for exports, since
  151. //the exports in here is amdefine exports.
  152. e = module.exports;
  153. m = module;
  154. r = makeRequire(requireFn, e, m, module.id);
  155. }
  156. //If there are dependencies, they are strings, so need
  157. //to convert them to dependency values.
  158. if (deps) {
  159. deps = deps.map(function (depName) {
  160. return r(depName);
  161. });
  162. }
  163. //Call the factory with the right dependencies.
  164. if (typeof factory === 'function') {
  165. result = factory.apply(m.exports, deps);
  166. } else {
  167. result = factory;
  168. }
  169. if (result !== undefined) {
  170. m.exports = result;
  171. if (id) {
  172. loaderCache[id] = m.exports;
  173. }
  174. }
  175. }
  176. stringRequire = function (systemRequire, exports, module, id, relId) {
  177. //Split the ID by a ! so that
  178. var index = id.indexOf('!'),
  179. originalId = id,
  180. prefix, plugin;
  181. if (index === -1) {
  182. id = normalize(id, relId);
  183. //Straight module lookup. If it is one of the special dependencies,
  184. //deal with it, otherwise, delegate to node.
  185. if (id === 'require') {
  186. return makeRequire(systemRequire, exports, module, relId);
  187. } else if (id === 'exports') {
  188. return exports;
  189. } else if (id === 'module') {
  190. return module;
  191. } else if (loaderCache.hasOwnProperty(id)) {
  192. return loaderCache[id];
  193. } else if (defineCache[id]) {
  194. runFactory.apply(null, defineCache[id]);
  195. return loaderCache[id];
  196. } else {
  197. if(systemRequire) {
  198. return systemRequire(originalId);
  199. } else {
  200. throw new Error('No module with ID: ' + id);
  201. }
  202. }
  203. } else {
  204. //There is a plugin in play.
  205. prefix = id.substring(0, index);
  206. id = id.substring(index + 1, id.length);
  207. plugin = stringRequire(systemRequire, exports, module, prefix, relId);
  208. if (plugin.normalize) {
  209. id = plugin.normalize(id, makeNormalize(relId));
  210. } else {
  211. //Normalize the ID normally.
  212. id = normalize(id, relId);
  213. }
  214. if (loaderCache[id]) {
  215. return loaderCache[id];
  216. } else {
  217. plugin.load(id, makeRequire(systemRequire, exports, module, relId), makeLoad(id), {});
  218. return loaderCache[id];
  219. }
  220. }
  221. };
  222. //Create a define function specific to the module asking for amdefine.
  223. function define(id, deps, factory) {
  224. if (Array.isArray(id)) {
  225. factory = deps;
  226. deps = id;
  227. id = undefined;
  228. } else if (typeof id !== 'string') {
  229. factory = id;
  230. id = deps = undefined;
  231. }
  232. if (deps && !Array.isArray(deps)) {
  233. factory = deps;
  234. deps = undefined;
  235. }
  236. if (!deps) {
  237. deps = ['require', 'exports', 'module'];
  238. }
  239. //Set up properties for this module. If an ID, then use
  240. //internal cache. If no ID, then use the external variables
  241. //for this node module.
  242. if (id) {
  243. //Put the module in deep freeze until there is a
  244. //require call for it.
  245. defineCache[id] = [id, deps, factory];
  246. } else {
  247. runFactory(id, deps, factory);
  248. }
  249. }
  250. //define.require, which has access to all the values in the
  251. //cache. Useful for AMD modules that all have IDs in the file,
  252. //but need to finally export a value to node based on one of those
  253. //IDs.
  254. define.require = function (id) {
  255. if (loaderCache[id]) {
  256. return loaderCache[id];
  257. }
  258. if (defineCache[id]) {
  259. runFactory.apply(null, defineCache[id]);
  260. return loaderCache[id];
  261. }
  262. };
  263. define.amd = {};
  264. return define;
  265. }
  266. module.exports = amdefine;