UNPKG

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