274 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| var Resolver = require("./Resolver");
 | |
| 
 | |
| var SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator");
 | |
| 
 | |
| var ParsePlugin = require("./ParsePlugin");
 | |
| var DescriptionFilePlugin = require("./DescriptionFilePlugin");
 | |
| var NextPlugin = require("./NextPlugin");
 | |
| var TryNextPlugin = require("./TryNextPlugin");
 | |
| var ModuleKindPlugin = require("./ModuleKindPlugin");
 | |
| var FileKindPlugin = require("./FileKindPlugin");
 | |
| var JoinRequestPlugin = require("./JoinRequestPlugin");
 | |
| var ModulesInHierachicDirectoriesPlugin = require("./ModulesInHierachicDirectoriesPlugin");
 | |
| var ModulesInRootPlugin = require("./ModulesInRootPlugin");
 | |
| var AliasPlugin = require("./AliasPlugin");
 | |
| var AliasFieldPlugin = require("./AliasFieldPlugin");
 | |
| var ConcordExtensionsPlugin = require("./ConcordExtensionsPlugin");
 | |
| var ConcordMainPlugin = require("./ConcordMainPlugin");
 | |
| var ConcordModulesPlugin = require("./ConcordModulesPlugin");
 | |
| var DirectoryExistsPlugin = require("./DirectoryExistsPlugin");
 | |
| var FileExistsPlugin = require("./FileExistsPlugin");
 | |
| var SymlinkPlugin = require("./SymlinkPlugin");
 | |
| var MainFieldPlugin = require("./MainFieldPlugin");
 | |
| var UseFilePlugin = require("./UseFilePlugin");
 | |
| var AppendPlugin = require("./AppendPlugin");
 | |
| var ResultPlugin = require("./ResultPlugin");
 | |
| var ModuleAppendPlugin = require("./ModuleAppendPlugin");
 | |
| var UnsafeCachePlugin = require("./UnsafeCachePlugin");
 | |
| 
 | |
| exports.createResolver = function(options) {
 | |
| 
 | |
| 	//// OPTIONS ////
 | |
| 
 | |
| 	// A list of directories to resolve modules from, can be absolute path or folder name
 | |
| 	var modules = options.modules || ["node_modules"];
 | |
| 
 | |
| 	// A list of description files to read from
 | |
| 	var descriptionFiles = options.descriptionFiles || ["package.json"];
 | |
| 
 | |
| 	// A list of additional resolve plugins which should be applied
 | |
| 	// The slice is there to create a copy, because otherwise pushing into plugins
 | |
| 	// changes the original options.plugins array, causing duplicate plugins
 | |
| 	var plugins = (options.plugins && options.plugins.slice()) || [];
 | |
| 
 | |
| 	// A list of main fields in description files
 | |
| 	var mainFields = options.mainFields || ["main"];
 | |
| 
 | |
| 	// A list of alias fields in description files
 | |
| 	var aliasFields = options.aliasFields || [];
 | |
| 
 | |
| 	// A list of main files in directories
 | |
| 	var mainFiles = options.mainFiles || ["index"];
 | |
| 
 | |
| 	// A list of extensions which should be tried for files
 | |
| 	var extensions = options.extensions || [".js", ".json", ".node"];
 | |
| 
 | |
| 	// Enforce that a extension from extensions must be used
 | |
| 	var enforceExtension = options.enforceExtension || false;
 | |
| 
 | |
| 	// A list of module extensions which should be tried for modules
 | |
| 	var moduleExtensions = options.moduleExtensions || [];
 | |
| 
 | |
| 	// Enforce that a extension from moduleExtensions must be used
 | |
| 	var enforceModuleExtension = options.enforceModuleExtension || false;
 | |
| 
 | |
| 	// A list of module alias configurations or an object which maps key to value
 | |
| 	var alias = options.alias || [];
 | |
| 
 | |
| 	// Resolve symlinks to their symlinked location
 | |
| 	var symlinks = typeof options.symlinks !== "undefined" ? options.symlinks : true;
 | |
| 
 | |
| 	// Resolve to a context instead of a file
 | |
| 	var resolveToContext = options.resolveToContext || false;
 | |
| 
 | |
| 	// Use this cache object to unsafely cache the successful requests
 | |
| 	var unsafeCache = options.unsafeCache || false;
 | |
| 
 | |
| 	// Whether or not the unsafeCache should include request context as part of the cache key.
 | |
| 	var cacheWithContext = typeof options.cacheWithContext !== "undefined" ? options.cacheWithContext : true;
 | |
| 
 | |
| 	// A function which decides whether a request should be cached or not.
 | |
| 	// an object is passed with `path` and `request` properties.
 | |
| 	var cachePredicate = options.cachePredicate || function() {
 | |
| 		return true;
 | |
| 	};
 | |
| 
 | |
| 	// The file system which should be used
 | |
| 	var fileSystem = options.fileSystem;
 | |
| 
 | |
| 	// Use only the sync variants of the file system calls
 | |
| 	var useSyncFileSystemCalls = options.useSyncFileSystemCalls;
 | |
| 
 | |
| 	// A prepared Resolver to which the plugins are attached
 | |
| 	var resolver = options.resolver;
 | |
| 
 | |
| 	//// options processing ////
 | |
| 
 | |
| 	if(!resolver) {
 | |
| 		resolver = new Resolver(useSyncFileSystemCalls ? new SyncAsyncFileSystemDecorator(fileSystem) : fileSystem);
 | |
| 	}
 | |
| 
 | |
| 	extensions = [].concat(extensions);
 | |
| 	moduleExtensions = [].concat(moduleExtensions);
 | |
| 
 | |
| 	modules = mergeFilteredToArray([].concat(modules), function(item) {
 | |
| 		return !isAbsolutePath(item);
 | |
| 	});
 | |
| 
 | |
| 	mainFields = mainFields.map(function(item) {
 | |
| 		if(typeof item === "string") {
 | |
| 			item = {
 | |
| 				name: item,
 | |
| 				forceRelative: true
 | |
| 			};
 | |
| 		}
 | |
| 		return item;
 | |
| 	});
 | |
| 
 | |
| 	if(typeof alias === "object" && !Array.isArray(alias)) {
 | |
| 		alias = Object.keys(alias).map(function(key) {
 | |
| 			var onlyModule = false;
 | |
| 			var obj = alias[key];
 | |
| 			if(/\$$/.test(key)) {
 | |
| 				onlyModule = true;
 | |
| 				key = key.substr(0, key.length - 1);
 | |
| 			}
 | |
| 			if(typeof obj === "string") {
 | |
| 				obj = {
 | |
| 					alias: obj
 | |
| 				};
 | |
| 			}
 | |
| 			obj = Object.assign({
 | |
| 				name: key,
 | |
| 				onlyModule: onlyModule
 | |
| 			}, obj);
 | |
| 			return obj;
 | |
| 		});
 | |
| 	}
 | |
| 
 | |
| 	if(unsafeCache && typeof unsafeCache !== "object") {
 | |
| 		unsafeCache = {};
 | |
| 	}
 | |
| 
 | |
| 	//// pipeline ////
 | |
| 
 | |
| 	// resolve
 | |
| 	if(unsafeCache) {
 | |
| 		plugins.push(new UnsafeCachePlugin("resolve", cachePredicate, unsafeCache, cacheWithContext, "new-resolve"));
 | |
| 		plugins.push(new ParsePlugin("new-resolve", "parsed-resolve"));
 | |
| 	} else {
 | |
| 		plugins.push(new ParsePlugin("resolve", "parsed-resolve"));
 | |
| 	}
 | |
| 
 | |
| 	// parsed-resolve
 | |
| 	plugins.push(new DescriptionFilePlugin("parsed-resolve", descriptionFiles, "described-resolve"));
 | |
| 	plugins.push(new NextPlugin("after-parsed-resolve", "described-resolve"));
 | |
| 
 | |
| 	// described-resolve
 | |
| 	alias.forEach(function(item) {
 | |
| 		plugins.push(new AliasPlugin("described-resolve", item, "resolve"));
 | |
| 	});
 | |
| 	plugins.push(new ConcordModulesPlugin("described-resolve", {}, "resolve"));
 | |
| 	aliasFields.forEach(function(item) {
 | |
| 		plugins.push(new AliasFieldPlugin("described-resolve", item, "resolve"));
 | |
| 	});
 | |
| 	plugins.push(new ModuleKindPlugin("after-described-resolve", "raw-module"));
 | |
| 	plugins.push(new JoinRequestPlugin("after-described-resolve", "relative"));
 | |
| 
 | |
| 	// raw-module
 | |
| 	moduleExtensions.forEach(function(item) {
 | |
| 		plugins.push(new ModuleAppendPlugin("raw-module", item, "module"));
 | |
| 	});
 | |
| 	if(!enforceModuleExtension)
 | |
| 		plugins.push(new TryNextPlugin("raw-module", null, "module"));
 | |
| 
 | |
| 	// module
 | |
| 	modules.forEach(function(item) {
 | |
| 		if(Array.isArray(item))
 | |
| 			plugins.push(new ModulesInHierachicDirectoriesPlugin("module", item, "resolve"));
 | |
| 		else
 | |
| 			plugins.push(new ModulesInRootPlugin("module", item, "resolve"));
 | |
| 	});
 | |
| 
 | |
| 	// relative
 | |
| 	plugins.push(new DescriptionFilePlugin("relative", descriptionFiles, "described-relative"));
 | |
| 	plugins.push(new NextPlugin("after-relative", "described-relative"));
 | |
| 
 | |
| 	// described-relative
 | |
| 	plugins.push(new FileKindPlugin("described-relative", "raw-file"));
 | |
| 	plugins.push(new TryNextPlugin("described-relative", "as directory", "directory"));
 | |
| 
 | |
| 	// directory
 | |
| 	plugins.push(new DirectoryExistsPlugin("directory", "existing-directory"));
 | |
| 
 | |
| 	if(resolveToContext) {
 | |
| 
 | |
| 		// existing-directory
 | |
| 		plugins.push(new NextPlugin("existing-directory", "resolved"));
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		// existing-directory
 | |
| 		plugins.push(new ConcordMainPlugin("existing-directory", {}, "resolve"));
 | |
| 		mainFields.forEach(function(item) {
 | |
| 			plugins.push(new MainFieldPlugin("existing-directory", item, "resolve"));
 | |
| 		});
 | |
| 		mainFiles.forEach(function(item) {
 | |
| 			plugins.push(new UseFilePlugin("existing-directory", item, "undescribed-raw-file"));
 | |
| 		});
 | |
| 
 | |
| 		// undescribed-raw-file
 | |
| 		plugins.push(new DescriptionFilePlugin("undescribed-raw-file", descriptionFiles, "raw-file"));
 | |
| 		plugins.push(new NextPlugin("after-undescribed-raw-file", "raw-file"));
 | |
| 
 | |
| 		// raw-file
 | |
| 		if(!enforceExtension)
 | |
| 			plugins.push(new TryNextPlugin("raw-file", "no extension", "file"));
 | |
| 		plugins.push(new ConcordExtensionsPlugin("raw-file", {}, "file"));
 | |
| 		extensions.forEach(function(item) {
 | |
| 			plugins.push(new AppendPlugin("raw-file", item, "file"));
 | |
| 		});
 | |
| 
 | |
| 		// file
 | |
| 		alias.forEach(function(item) {
 | |
| 			plugins.push(new AliasPlugin("file", item, "resolve"));
 | |
| 		});
 | |
| 		plugins.push(new ConcordModulesPlugin("file", {}, "resolve"));
 | |
| 		aliasFields.forEach(function(item) {
 | |
| 			plugins.push(new AliasFieldPlugin("file", item, "resolve"));
 | |
| 		});
 | |
| 		if(symlinks)
 | |
| 			plugins.push(new SymlinkPlugin("file", "relative"));
 | |
| 		plugins.push(new FileExistsPlugin("file", "existing-file"));
 | |
| 
 | |
| 		// existing-file
 | |
| 		plugins.push(new NextPlugin("existing-file", "resolved"));
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	// resolved
 | |
| 	plugins.push(new ResultPlugin("resolved"));
 | |
| 
 | |
| 	//// RESOLVER ////
 | |
| 
 | |
| 	plugins.forEach(function(plugin) {
 | |
| 		resolver.apply(plugin);
 | |
| 	});
 | |
| 	return resolver;
 | |
| };
 | |
| 
 | |
| function mergeFilteredToArray(array, filter) {
 | |
| 	return array.reduce(function(array, item) {
 | |
| 		if(filter(item)) {
 | |
| 			var lastElement = array[array.length - 1];
 | |
| 			if(Array.isArray(lastElement)) {
 | |
| 				lastElement.push(item);
 | |
| 			} else {
 | |
| 				array.push([item]);
 | |
| 			}
 | |
| 			return array;
 | |
| 		} else {
 | |
| 			array.push(item);
 | |
| 			return array;
 | |
| 		}
 | |
| 	}, []);
 | |
| }
 | |
| 
 | |
| function isAbsolutePath(path) {
 | |
| 	return /^[A-Z]:|^\//.test(path);
 | |
| }
 |