Resolver.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var Tapable = require("tapable");
  6. var createInnerCallback = require("./createInnerCallback");
  7. function Resolver(fileSystem) {
  8. Tapable.call(this);
  9. this.fileSystem = fileSystem;
  10. }
  11. module.exports = Resolver;
  12. Resolver.prototype = Object.create(Tapable.prototype);
  13. Resolver.prototype.constructor = Resolver;
  14. Resolver.prototype.resolveSync = function resolveSync(context, path, request) {
  15. var err, result, sync = false;
  16. this.resolve(context, path, request, function(e, r) {
  17. err = e;
  18. result = r;
  19. sync = true;
  20. });
  21. if(!sync) throw new Error("Cannot 'resolveSync' because the fileSystem is not sync. Use 'resolve'!");
  22. if(err) throw err;
  23. return result;
  24. };
  25. Resolver.prototype.resolve = function resolve(context, path, request, callback) {
  26. if(arguments.length === 3) {
  27. throw new Error("Signature changed: context parameter added");
  28. }
  29. var resolver = this;
  30. var obj = {
  31. context: context,
  32. path: path,
  33. request: request
  34. };
  35. var localMissing;
  36. var log;
  37. var message = "resolve '" + request + "' in '" + path + "'";
  38. function writeLog(msg) {
  39. log.push(msg);
  40. }
  41. function logAsString() {
  42. return log.join("\n");
  43. }
  44. function onError(err, result) {
  45. if(callback.log) {
  46. for(var i = 0; i < log.length; i++)
  47. callback.log(log[i]);
  48. }
  49. if(err) return callback(err);
  50. var error = new Error("Can't " + message);
  51. error.details = logAsString();
  52. error.missing = localMissing;
  53. resolver.applyPlugins("no-resolve", obj, error);
  54. return callback(error);
  55. }
  56. function onResolve(err, result) {
  57. if(!err && result) {
  58. return callback(null, result.path === false ? false : result.path + (result.query || ""), result);
  59. }
  60. localMissing = [];
  61. log = [];
  62. return resolver.doResolve("resolve", obj, message, createInnerCallback(onError, {
  63. log: writeLog,
  64. missing: localMissing,
  65. stack: callback.stack
  66. }));
  67. }
  68. onResolve.missing = callback.missing;
  69. onResolve.stack = callback.stack;
  70. return this.doResolve("resolve", obj, message, onResolve);
  71. };
  72. Resolver.prototype.doResolve = function doResolve(type, request, message, callback) {
  73. var resolver = this;
  74. var stackLine = type + ": (" + request.path + ") " +
  75. (request.request || "") + (request.query || "") +
  76. (request.directory ? " directory" : "") +
  77. (request.module ? " module" : "");
  78. var newStack = [stackLine];
  79. if(callback.stack) {
  80. newStack = callback.stack.concat(newStack);
  81. if(callback.stack.indexOf(stackLine) >= 0) {
  82. // Prevent recursion
  83. var recursionError = new Error("Recursion in resolving\nStack:\n " + newStack.join("\n "));
  84. recursionError.recursion = true;
  85. if(callback.log) callback.log("abort resolving because of recursion");
  86. return callback(recursionError);
  87. }
  88. }
  89. resolver.applyPlugins("resolve-step", type, request);
  90. var beforePluginName = "before-" + type;
  91. if(resolver.hasPlugins(beforePluginName)) {
  92. resolver.applyPluginsAsyncSeriesBailResult1(beforePluginName, request, createInnerCallback(beforeInnerCallback, {
  93. log: callback.log,
  94. missing: callback.missing,
  95. stack: newStack
  96. }, message && ("before " + message), true));
  97. } else {
  98. runNormal();
  99. }
  100. function beforeInnerCallback(err, result) {
  101. if(arguments.length > 0) {
  102. if(err) return callback(err);
  103. if(result) return callback(null, result);
  104. return callback();
  105. }
  106. runNormal();
  107. }
  108. function runNormal() {
  109. if(resolver.hasPlugins(type)) {
  110. return resolver.applyPluginsAsyncSeriesBailResult1(type, request, createInnerCallback(innerCallback, {
  111. log: callback.log,
  112. missing: callback.missing,
  113. stack: newStack
  114. }, message));
  115. } else {
  116. runAfter();
  117. }
  118. }
  119. function innerCallback(err, result) {
  120. if(arguments.length > 0) {
  121. if(err) return callback(err);
  122. if(result) return callback(null, result);
  123. return callback();
  124. }
  125. runAfter();
  126. }
  127. function runAfter() {
  128. var afterPluginName = "after-" + type;
  129. if(resolver.hasPlugins(afterPluginName)) {
  130. return resolver.applyPluginsAsyncSeriesBailResult1(afterPluginName, request, createInnerCallback(afterInnerCallback, {
  131. log: callback.log,
  132. missing: callback.missing,
  133. stack: newStack
  134. }, message && ("after " + message), true));
  135. } else {
  136. callback();
  137. }
  138. }
  139. function afterInnerCallback(err, result) {
  140. if(arguments.length > 0) {
  141. if(err) return callback(err);
  142. if(result) return callback(null, result);
  143. return callback();
  144. }
  145. return callback();
  146. }
  147. };
  148. Resolver.prototype.parse = function parse(identifier) {
  149. if(identifier === "") return null;
  150. var part = {
  151. request: "",
  152. query: "",
  153. module: false,
  154. directory: false,
  155. file: false
  156. };
  157. var idxQuery = identifier.indexOf("?");
  158. if(idxQuery === 0) {
  159. part.query = identifier;
  160. } else if(idxQuery > 0) {
  161. part.request = identifier.slice(0, idxQuery);
  162. part.query = identifier.slice(idxQuery);
  163. } else {
  164. part.request = identifier;
  165. }
  166. if(part.request) {
  167. part.module = this.isModule(part.request);
  168. part.directory = this.isDirectory(part.request);
  169. if(part.directory) {
  170. part.request = part.request.substr(0, part.request.length - 1);
  171. }
  172. }
  173. return part;
  174. };
  175. var notModuleRegExp = /^\.$|^\.[\\\/]|^\.\.$|^\.\.[\/\\]|^\/|^[A-Z]:[\\\/]/i;
  176. Resolver.prototype.isModule = function isModule(path) {
  177. return !notModuleRegExp.test(path);
  178. };
  179. var directoryRegExp = /[\/\\]$/i;
  180. Resolver.prototype.isDirectory = function isDirectory(path) {
  181. return directoryRegExp.test(path);
  182. };
  183. var memoryFsJoin = require("memory-fs/lib/join");
  184. var memoizedJoin = new Map();
  185. Resolver.prototype.join = function(path, request) {
  186. var cacheEntry;
  187. var pathCache = memoizedJoin.get(path);
  188. if(typeof pathCache === "undefined") {
  189. memoizedJoin.set(path, pathCache = new Map());
  190. } else {
  191. cacheEntry = pathCache.get(request);
  192. if(typeof cacheEntry !== "undefined")
  193. return cacheEntry;
  194. }
  195. cacheEntry = memoryFsJoin(path, request);
  196. pathCache.set(request, cacheEntry);
  197. return cacheEntry;
  198. };
  199. Resolver.prototype.normalize = require("memory-fs/lib/normalize");