amdefine.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /** vim: et:ts=4:sw=4:sts=4
  2. * @license amdefine 0.0.1 Copyright (c) 2011, 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 strict: false, nomen: false, plusplus: false */
  7. /*global module, process, require: true */
  8. var path = require('path'),
  9. loaderCache = {},
  10. makeRequire;
  11. // Null out require for this file so that it is not accidentally used
  12. // below, where module.require should be used instead.
  13. require = null;
  14. /**
  15. * Given a relative module name, like ./something, normalize it to
  16. * a real name that can be mapped to a path.
  17. * @param {String} name the relative name
  18. * @param {String} baseName a real name that the name arg is relative
  19. * to.
  20. * @returns {String} normalized name
  21. */
  22. function normalize(name, baseName) {
  23. return path.normalize(path.join(baseName, name));
  24. }
  25. /**
  26. * Create the normalize() function passed to a loader plugin's
  27. * normalize method.
  28. */
  29. function makeNormalize(relName) {
  30. return function (name) {
  31. return normalize(name, relName);
  32. };
  33. }
  34. function makeLoad(id) {
  35. function load(value) {
  36. loaderCache[id] = value;
  37. }
  38. load.fromText = function (id, text) {
  39. //This one is difficult because the text can/probably uses
  40. //define, and any relative paths and requires should be relative
  41. //to that id was it would be found on disk. But this would require
  42. //bootstrapping a module/require fairly deeply from node core.
  43. //Not sure how best to go about that yet.
  44. throw new Error('amdefine does not implement load.fromText');
  45. };
  46. return load;
  47. }
  48. function stringRequire(module, id) {
  49. //Split the ID by a ! so that
  50. var index = id.indexOf('!'),
  51. relId = path.dirname(module.filename),
  52. prefix, plugin;
  53. if (index === -1) {
  54. //Straight module lookup. If it is one of the special dependencies,
  55. //deal with it, otherwise, delegate to node.
  56. if (id === 'require') {
  57. return makeRequire(module);
  58. } else if (id === 'exports') {
  59. return module.exports;
  60. } else if (id === 'module') {
  61. return module;
  62. } else {
  63. return module.require(id);
  64. }
  65. } else {
  66. //There is a plugin in play.
  67. prefix = id.substring(0, index);
  68. id = id.substring(index + 1, id.length);
  69. plugin = module.require(prefix);
  70. if (plugin.normalize) {
  71. id = plugin.normalize(id, makeNormalize(relId));
  72. } else {
  73. //Normalize the ID normally.
  74. id = normalize(id, relId);
  75. }
  76. if (loaderCache[id]) {
  77. return loaderCache[id];
  78. } else {
  79. plugin.load(id, makeRequire(module), makeLoad(id), {});
  80. return loaderCache[id];
  81. }
  82. }
  83. }
  84. makeRequire = function (module) {
  85. function amdRequire(deps, callback) {
  86. if (typeof deps === 'string') {
  87. //Synchronous, single module require('')
  88. return stringRequire(module, deps);
  89. } else {
  90. //Array of dependencies with a callback.
  91. //Convert the dependencies to modules.
  92. deps = deps.map(function (depName) {
  93. return stringRequire(module, depName);
  94. });
  95. //Wait for next tick to call back the require call.
  96. process.nextTick(function () {
  97. callback.apply(null, deps);
  98. });
  99. //Keeps strict checking in komodo happy.
  100. return undefined;
  101. }
  102. }
  103. amdRequire.toUrl = function (filePath) {
  104. if (filePath.indexOf('.') === 0) {
  105. return normalize(filePath, path.dirname(module.filename));
  106. } else {
  107. return filePath;
  108. }
  109. };
  110. return amdRequire;
  111. };
  112. function amdefine(module) {
  113. var alreadyCalled = false;
  114. //Create a define function specific to the module asking for amdefine.
  115. function define() {
  116. var args = arguments,
  117. factory = args[args.length - 1],
  118. isFactoryFunction = (typeof factory === 'function'),
  119. deps, result;
  120. //Only support one define call per file
  121. if (alreadyCalled) {
  122. throw new Error('amdefine cannot be called more than once per file.');
  123. }
  124. alreadyCalled = true;
  125. //Grab array of dependencies if it is there.
  126. if (args.length > 1) {
  127. deps = args[args.length - 2];
  128. if (!Array.isArray(deps)) {
  129. //deps is not an array, may be an ID. Discard it.
  130. deps = null;
  131. }
  132. }
  133. //If there are dependencies, they are strings, so need
  134. //to convert them to dependency values.
  135. if (deps) {
  136. deps = deps.map(function (depName) {
  137. return stringRequire(module, depName);
  138. });
  139. } else if (isFactoryFunction) {
  140. //Pass in the standard require, exports, module
  141. deps = [makeRequire(module), module.exports, module];
  142. }
  143. if (!isFactoryFunction) {
  144. //Factory is an object that should just be used for the define call.
  145. module.exports = factory;
  146. } else {
  147. //Call the factory with the right dependencies.
  148. result = factory.apply(module.exports, deps);
  149. if (result !== undefined) {
  150. module.exports = result;
  151. }
  152. }
  153. }
  154. define.amd = {};
  155. return define;
  156. }
  157. module.exports = amdefine;