amdefine.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /*jslint strict: false, nomen: false, plusplus: false */
  2. /*global require, module, process */
  3. var loaderCache = {},
  4. makeRequire;
  5. /**
  6. * Given a relative module name, like ./something, normalize it to
  7. * a real name that can be mapped to a path.
  8. * @param {String} name the relative name
  9. * @param {String} baseName a real name that the name arg is relative
  10. * to.
  11. * @returns {String} normalized name
  12. */
  13. function normalize(name, baseName) {
  14. //Adjust any relative paths.
  15. if (name && name.charAt(0) === ".") {
  16. //If have a base name, try to normalize against it,
  17. //otherwise, assume it is a top-level require that will
  18. //be relative to baseUrl in the end.
  19. if (baseName) {
  20. //Convert baseName to array, and lop off the last part,
  21. //so that . matches that "directory" and not name of the baseName's
  22. //module. For instance, baseName of "one/two/three", maps to
  23. //"one/two/three.js", but we want the directory, "one/two" for
  24. //this normalization.
  25. baseName = baseName.split("/");
  26. baseName = baseName.slice(0, baseName.length - 1);
  27. name = baseName.concat(name.split("/"));
  28. //start trimDots
  29. var i, part;
  30. for (i = 0; (part = name[i]); i++) {
  31. if (part === ".") {
  32. name.splice(i, 1);
  33. i -= 1;
  34. } else if (part === "..") {
  35. if (i === 1 && (name[2] === '..' || name[0] === '..')) {
  36. //End of the line. Keep at least one non-dot
  37. //path segment at the front so it can be mapped
  38. //correctly to disk. Otherwise, there is likely
  39. //no path mapping for a path starting with '..'.
  40. //This can still fail, but catches the most reasonable
  41. //uses of ..
  42. break;
  43. } else if (i > 0) {
  44. name.splice(i - 1, 2);
  45. i -= 2;
  46. }
  47. }
  48. }
  49. //end trimDots
  50. name = name.join("/");
  51. }
  52. }
  53. return name;
  54. }
  55. function makeNormalize(relName) {
  56. return function (name) {
  57. return normalize(name, relName);
  58. };
  59. }
  60. function stringRequire(module, id) {
  61. //Split the ID by a ! so that
  62. var index = id.indexOf('!'),
  63. relId = module.id,
  64. prefix, plugin;
  65. if (index === -1) {
  66. //Straight module lookup. If it is one of the special dependencies,
  67. //deal with it, otherwise, delegate to node.
  68. if (id === 'require') {
  69. return makeRequire(module);
  70. } else if (id === 'exports') {
  71. return module.exports;
  72. } else if (id === 'module') {
  73. return module;
  74. } else {
  75. return module.require(id);
  76. }
  77. } else {
  78. //There is a plugin in play.
  79. prefix = id.substring(0, index);
  80. id = id.substring(index, id.length);
  81. plugin = require(prefix);
  82. if (plugin.normalize) {
  83. id = plugin.normalize(id, makeNormalize(relId));
  84. } else {
  85. //Normalize the ID normally.
  86. id = normalize(id, relId);
  87. }
  88. if (loaderCache[id]) {
  89. return loaderCache[id];
  90. } else {
  91. plugin.load(id, makeRequire(module), function (value) {
  92. loaderCache[id] = value;
  93. }, {});
  94. return loaderCache[id];
  95. }
  96. }
  97. }
  98. makeRequire = function (module) {
  99. return function (deps, callback) {
  100. if (typeof deps === 'string') {
  101. //Synchronous, single module require('')
  102. return stringRequire(module, deps);
  103. } else {
  104. //Array of dependencies with a callback.
  105. //Convert the dependencies to modules.
  106. deps = deps.map(function (depName) {
  107. return stringRequire(module, depName);
  108. });
  109. //Wait for next tick to call back the require call.
  110. process.nextTick(function () {
  111. callback.apply(null, deps);
  112. });
  113. //Keeps strict checking in komodo happy.
  114. return undefined;
  115. }
  116. };
  117. };
  118. function amdefine(module) {
  119. var alreadyCalled = false;
  120. //Create a define function specific to the module asking for amdefine.
  121. function define() {
  122. var args = arguments,
  123. factory = args[args.length - 1],
  124. isFactoryFunction = (typeof factory === 'function'),
  125. deps, result;
  126. //Only support one define call per file
  127. if (alreadyCalled) {
  128. throw new Error('amdefine cannot be called more than once per file.');
  129. }
  130. alreadyCalled = true;
  131. //Grab array of dependencies if it is there.
  132. if (args.length > 1) {
  133. deps = args[args.length - 2];
  134. if (!Array.isArray(deps)) {
  135. //deps is not an array, may be an ID. Discard it.
  136. deps = null;
  137. }
  138. }
  139. //If there are dependencies, they are strings, so need
  140. //to convert them to dependency values.
  141. if (deps) {
  142. deps = deps.map(function (depName) {
  143. return stringRequire(module, depName);
  144. });
  145. } else if (isFactoryFunction) {
  146. //Pass in the standard require, exports, module
  147. deps = [makeRequire(module), module.exports, module];
  148. }
  149. if (!isFactoryFunction) {
  150. //Factory is an object that should just be used for the define call.
  151. module.exports = factory;
  152. } else {
  153. //Call the factory with the right dependencies.
  154. result = factory.apply(module.exports, deps);
  155. if (result !== undefined) {
  156. module.exports = result;
  157. }
  158. }
  159. }
  160. define.amd = {};
  161. return define;
  162. }
  163. module.exports = amdefine;