您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ResolverFactory.js 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Resolver = require("./Resolver");
  7. const SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator");
  8. const ParsePlugin = require("./ParsePlugin");
  9. const DescriptionFilePlugin = require("./DescriptionFilePlugin");
  10. const NextPlugin = require("./NextPlugin");
  11. const TryNextPlugin = require("./TryNextPlugin");
  12. const ModuleKindPlugin = require("./ModuleKindPlugin");
  13. const FileKindPlugin = require("./FileKindPlugin");
  14. const JoinRequestPlugin = require("./JoinRequestPlugin");
  15. const ModulesInHierachicDirectoriesPlugin = require("./ModulesInHierachicDirectoriesPlugin");
  16. const ModulesInRootPlugin = require("./ModulesInRootPlugin");
  17. const AliasPlugin = require("./AliasPlugin");
  18. const AliasFieldPlugin = require("./AliasFieldPlugin");
  19. const ConcordExtensionsPlugin = require("./ConcordExtensionsPlugin");
  20. const ConcordMainPlugin = require("./ConcordMainPlugin");
  21. const ConcordModulesPlugin = require("./ConcordModulesPlugin");
  22. const DirectoryExistsPlugin = require("./DirectoryExistsPlugin");
  23. const FileExistsPlugin = require("./FileExistsPlugin");
  24. const SymlinkPlugin = require("./SymlinkPlugin");
  25. const MainFieldPlugin = require("./MainFieldPlugin");
  26. const UseFilePlugin = require("./UseFilePlugin");
  27. const AppendPlugin = require("./AppendPlugin");
  28. const ResultPlugin = require("./ResultPlugin");
  29. const ModuleAppendPlugin = require("./ModuleAppendPlugin");
  30. const UnsafeCachePlugin = require("./UnsafeCachePlugin");
  31. exports.createResolver = function(options) {
  32. //// OPTIONS ////
  33. // A list of directories to resolve modules from, can be absolute path or folder name
  34. let modules = options.modules || ["node_modules"];
  35. // A list of description files to read from
  36. const descriptionFiles = options.descriptionFiles || ["package.json"];
  37. // A list of additional resolve plugins which should be applied
  38. // The slice is there to create a copy, because otherwise pushing into plugins
  39. // changes the original options.plugins array, causing duplicate plugins
  40. const plugins = (options.plugins && options.plugins.slice()) || [];
  41. // A list of main fields in description files
  42. let mainFields = options.mainFields || ["main"];
  43. // A list of alias fields in description files
  44. const aliasFields = options.aliasFields || [];
  45. // A list of main files in directories
  46. const mainFiles = options.mainFiles || ["index"];
  47. // A list of extensions which should be tried for files
  48. let extensions = options.extensions || [".js", ".json", ".node"];
  49. // Enforce that a extension from extensions must be used
  50. const enforceExtension = options.enforceExtension || false;
  51. // A list of module extensions which should be tried for modules
  52. let moduleExtensions = options.moduleExtensions || [];
  53. // Enforce that a extension from moduleExtensions must be used
  54. const enforceModuleExtension = options.enforceModuleExtension || false;
  55. // A list of module alias configurations or an object which maps key to value
  56. let alias = options.alias || [];
  57. // Resolve symlinks to their symlinked location
  58. const symlinks = typeof options.symlinks !== "undefined" ? options.symlinks : true;
  59. // Resolve to a context instead of a file
  60. const resolveToContext = options.resolveToContext || false;
  61. // Use this cache object to unsafely cache the successful requests
  62. let unsafeCache = options.unsafeCache || false;
  63. // Whether or not the unsafeCache should include request context as part of the cache key.
  64. const cacheWithContext = typeof options.cacheWithContext !== "undefined" ? options.cacheWithContext : true;
  65. // Enable concord description file instructions
  66. const enableConcord = options.concord || false;
  67. // A function which decides whether a request should be cached or not.
  68. // an object is passed with `path` and `request` properties.
  69. const cachePredicate = options.cachePredicate || function() {
  70. return true;
  71. };
  72. // The file system which should be used
  73. const fileSystem = options.fileSystem;
  74. // Use only the sync constiants of the file system calls
  75. const useSyncFileSystemCalls = options.useSyncFileSystemCalls;
  76. // A prepared Resolver to which the plugins are attached
  77. let resolver = options.resolver;
  78. //// options processing ////
  79. if(!resolver) {
  80. resolver = new Resolver(useSyncFileSystemCalls ? new SyncAsyncFileSystemDecorator(fileSystem) : fileSystem);
  81. }
  82. extensions = [].concat(extensions);
  83. moduleExtensions = [].concat(moduleExtensions);
  84. modules = mergeFilteredToArray([].concat(modules), item => {
  85. return !isAbsolutePath(item);
  86. });
  87. mainFields = mainFields.map(item => {
  88. if(typeof item === "string" || Array.isArray(item)) {
  89. item = {
  90. name: item,
  91. forceRelative: true
  92. };
  93. }
  94. return item;
  95. });
  96. if(typeof alias === "object" && !Array.isArray(alias)) {
  97. alias = Object.keys(alias).map(key => {
  98. let onlyModule = false;
  99. let obj = alias[key];
  100. if(/\$$/.test(key)) {
  101. onlyModule = true;
  102. key = key.substr(0, key.length - 1);
  103. }
  104. if(typeof obj === "string") {
  105. obj = {
  106. alias: obj
  107. };
  108. }
  109. obj = Object.assign({
  110. name: key,
  111. onlyModule: onlyModule
  112. }, obj);
  113. return obj;
  114. });
  115. }
  116. if(unsafeCache && typeof unsafeCache !== "object") {
  117. unsafeCache = {};
  118. }
  119. //// pipeline ////
  120. resolver.ensureHook("resolve");
  121. resolver.ensureHook("parsedResolve");
  122. resolver.ensureHook("describedResolve");
  123. resolver.ensureHook("rawModule");
  124. resolver.ensureHook("module");
  125. resolver.ensureHook("relative");
  126. resolver.ensureHook("describedRelative");
  127. resolver.ensureHook("directory");
  128. resolver.ensureHook("existingDirectory");
  129. resolver.ensureHook("undescribedRawFile");
  130. resolver.ensureHook("rawFile");
  131. resolver.ensureHook("file");
  132. resolver.ensureHook("existingFile");
  133. resolver.ensureHook("resolved");
  134. // resolve
  135. if(unsafeCache) {
  136. plugins.push(new UnsafeCachePlugin("resolve", cachePredicate, unsafeCache, cacheWithContext, "new-resolve"));
  137. plugins.push(new ParsePlugin("new-resolve", "parsed-resolve"));
  138. } else {
  139. plugins.push(new ParsePlugin("resolve", "parsed-resolve"));
  140. }
  141. // parsed-resolve
  142. plugins.push(new DescriptionFilePlugin("parsed-resolve", descriptionFiles, "described-resolve"));
  143. plugins.push(new NextPlugin("after-parsed-resolve", "described-resolve"));
  144. // described-resolve
  145. if(alias.length > 0)
  146. plugins.push(new AliasPlugin("described-resolve", alias, "resolve"));
  147. if(enableConcord) {
  148. plugins.push(new ConcordModulesPlugin("described-resolve", {}, "resolve"));
  149. }
  150. aliasFields.forEach(item => {
  151. plugins.push(new AliasFieldPlugin("described-resolve", item, "resolve"));
  152. });
  153. plugins.push(new ModuleKindPlugin("after-described-resolve", "raw-module"));
  154. plugins.push(new JoinRequestPlugin("after-described-resolve", "relative"));
  155. // raw-module
  156. moduleExtensions.forEach(item => {
  157. plugins.push(new ModuleAppendPlugin("raw-module", item, "module"));
  158. });
  159. if(!enforceModuleExtension)
  160. plugins.push(new TryNextPlugin("raw-module", null, "module"));
  161. // module
  162. modules.forEach(item => {
  163. if(Array.isArray(item))
  164. plugins.push(new ModulesInHierachicDirectoriesPlugin("module", item, "resolve"));
  165. else
  166. plugins.push(new ModulesInRootPlugin("module", item, "resolve"));
  167. });
  168. // relative
  169. plugins.push(new DescriptionFilePlugin("relative", descriptionFiles, "described-relative"));
  170. plugins.push(new NextPlugin("after-relative", "described-relative"));
  171. // described-relative
  172. plugins.push(new FileKindPlugin("described-relative", "raw-file"));
  173. plugins.push(new TryNextPlugin("described-relative", "as directory", "directory"));
  174. // directory
  175. plugins.push(new DirectoryExistsPlugin("directory", "existing-directory"));
  176. if(resolveToContext) {
  177. // existing-directory
  178. plugins.push(new NextPlugin("existing-directory", "resolved"));
  179. } else {
  180. // existing-directory
  181. if(enableConcord) {
  182. plugins.push(new ConcordMainPlugin("existing-directory", {}, "resolve"));
  183. }
  184. mainFields.forEach(item => {
  185. plugins.push(new MainFieldPlugin("existing-directory", item, "resolve"));
  186. });
  187. mainFiles.forEach(item => {
  188. plugins.push(new UseFilePlugin("existing-directory", item, "undescribed-raw-file"));
  189. });
  190. // undescribed-raw-file
  191. plugins.push(new DescriptionFilePlugin("undescribed-raw-file", descriptionFiles, "raw-file"));
  192. plugins.push(new NextPlugin("after-undescribed-raw-file", "raw-file"));
  193. // raw-file
  194. if(!enforceExtension) {
  195. plugins.push(new TryNextPlugin("raw-file", "no extension", "file"));
  196. }
  197. if(enableConcord) {
  198. plugins.push(new ConcordExtensionsPlugin("raw-file", {}, "file"));
  199. }
  200. extensions.forEach(item => {
  201. plugins.push(new AppendPlugin("raw-file", item, "file"));
  202. });
  203. // file
  204. if(alias.length > 0)
  205. plugins.push(new AliasPlugin("file", alias, "resolve"));
  206. if(enableConcord) {
  207. plugins.push(new ConcordModulesPlugin("file", {}, "resolve"));
  208. }
  209. aliasFields.forEach(item => {
  210. plugins.push(new AliasFieldPlugin("file", item, "resolve"));
  211. });
  212. if(symlinks)
  213. plugins.push(new SymlinkPlugin("file", "relative"));
  214. plugins.push(new FileExistsPlugin("file", "existing-file"));
  215. // existing-file
  216. plugins.push(new NextPlugin("existing-file", "resolved"));
  217. }
  218. // resolved
  219. plugins.push(new ResultPlugin(resolver.hooks.resolved));
  220. //// RESOLVER ////
  221. plugins.forEach(plugin => {
  222. plugin.apply(resolver);
  223. });
  224. return resolver;
  225. };
  226. function mergeFilteredToArray(array, filter) {
  227. return array.reduce((array, item) => {
  228. if(filter(item)) {
  229. const lastElement = array[array.length - 1];
  230. if(Array.isArray(lastElement)) {
  231. lastElement.push(item);
  232. } else {
  233. array.push([item]);
  234. }
  235. return array;
  236. } else {
  237. array.push(item);
  238. return array;
  239. }
  240. }, []);
  241. }
  242. function isAbsolutePath(path) {
  243. return /^[A-Z]:|^\//.test(path);
  244. }