|
|
/* MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra */ "use strict";
const Resolver = require("./Resolver");
const SyncAsyncFileSystemDecorator = require("./SyncAsyncFileSystemDecorator");
const ParsePlugin = require("./ParsePlugin"); const DescriptionFilePlugin = require("./DescriptionFilePlugin"); const NextPlugin = require("./NextPlugin"); const TryNextPlugin = require("./TryNextPlugin"); const ModuleKindPlugin = require("./ModuleKindPlugin"); const FileKindPlugin = require("./FileKindPlugin"); const JoinRequestPlugin = require("./JoinRequestPlugin"); const ModulesInHierachicDirectoriesPlugin = require("./ModulesInHierachicDirectoriesPlugin"); const ModulesInRootPlugin = require("./ModulesInRootPlugin"); const AliasPlugin = require("./AliasPlugin"); const AliasFieldPlugin = require("./AliasFieldPlugin"); const ConcordExtensionsPlugin = require("./ConcordExtensionsPlugin"); const ConcordMainPlugin = require("./ConcordMainPlugin"); const ConcordModulesPlugin = require("./ConcordModulesPlugin"); const DirectoryExistsPlugin = require("./DirectoryExistsPlugin"); const FileExistsPlugin = require("./FileExistsPlugin"); const SymlinkPlugin = require("./SymlinkPlugin"); const MainFieldPlugin = require("./MainFieldPlugin"); const UseFilePlugin = require("./UseFilePlugin"); const AppendPlugin = require("./AppendPlugin"); const RootPlugin = require("./RootPlugin"); const RestrictionsPlugin = require("./RestrictionsPlugin"); const ResultPlugin = require("./ResultPlugin"); const ModuleAppendPlugin = require("./ModuleAppendPlugin"); const UnsafeCachePlugin = require("./UnsafeCachePlugin");
exports.createResolver = function(options) { //// OPTIONS ////
// A list of directories to resolve modules from, can be absolute path or folder name
let modules = options.modules || ["node_modules"];
// A list of description files to read from
const 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
const plugins = (options.plugins && options.plugins.slice()) || [];
// A list of main fields in description files
let mainFields = options.mainFields || ["main"];
// A list of alias fields in description files
const aliasFields = options.aliasFields || [];
// A list of main files in directories
const mainFiles = options.mainFiles || ["index"];
// A list of extensions which should be tried for files
let extensions = options.extensions || [".js", ".json", ".node"];
// Enforce that a extension from extensions must be used
const enforceExtension = options.enforceExtension || false;
// A list of module extensions which should be tried for modules
let moduleExtensions = options.moduleExtensions || [];
// Enforce that a extension from moduleExtensions must be used
const enforceModuleExtension = options.enforceModuleExtension || false;
// A list of module alias configurations or an object which maps key to value
let alias = options.alias || [];
// Resolve symlinks to their symlinked location
const symlinks = typeof options.symlinks !== "undefined" ? options.symlinks : true;
// Resolve to a context instead of a file
const resolveToContext = options.resolveToContext || false;
// A list of root paths
const roots = options.roots || [];
// Ignore errors happening when resolving roots
const ignoreRootsErrors = options.ignoreRootsErrors || false;
// Prefer to resolve server-relative urls as absolute paths before falling back to resolve in roots
const preferAbsolute = options.preferAbsolute || false;
const restrictions = options.restrictions || [];
// Use this cache object to unsafely cache the successful requests
let unsafeCache = options.unsafeCache || false;
// Whether or not the unsafeCache should include request context as part of the cache key.
const cacheWithContext = typeof options.cacheWithContext !== "undefined" ? options.cacheWithContext : true;
// Enable concord description file instructions
const enableConcord = options.concord || false;
// A function which decides whether a request should be cached or not.
// an object is passed with `path` and `request` properties.
const cachePredicate = options.cachePredicate || function() { return true; };
// The file system which should be used
const fileSystem = options.fileSystem;
// Use only the sync constiants of the file system calls
const useSyncFileSystemCalls = options.useSyncFileSystemCalls;
// A prepared Resolver to which the plugins are attached
let 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), item => { return !isAbsolutePath(item); });
mainFields = mainFields.map(item => { if (typeof item === "string" || Array.isArray(item)) { item = { name: item, forceRelative: true }; } return item; });
if (typeof alias === "object" && !Array.isArray(alias)) { alias = Object.keys(alias).map(key => { let onlyModule = false; let 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 ////
resolver.ensureHook("resolve"); resolver.ensureHook("parsedResolve"); resolver.ensureHook("describedResolve"); resolver.ensureHook("rawModule"); resolver.ensureHook("module"); resolver.ensureHook("relative"); resolver.ensureHook("describedRelative"); resolver.ensureHook("directory"); resolver.ensureHook("existingDirectory"); resolver.ensureHook("undescribedRawFile"); resolver.ensureHook("rawFile"); resolver.ensureHook("file"); resolver.ensureHook("existingFile"); resolver.ensureHook("resolved");
// 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
if (alias.length > 0) plugins.push(new AliasPlugin("described-resolve", alias, "resolve")); if (enableConcord) { plugins.push(new ConcordModulesPlugin("described-resolve", {}, "resolve")); } aliasFields.forEach(item => { plugins.push(new AliasFieldPlugin("described-resolve", item, "resolve")); }); plugins.push(new ModuleKindPlugin("after-described-resolve", "raw-module")); if (preferAbsolute) { plugins.push(new JoinRequestPlugin("after-described-resolve", "relative")); } roots.forEach(root => { plugins.push( new RootPlugin( "after-described-resolve", root, "relative", ignoreRootsErrors ) ); }); if (!preferAbsolute) { plugins.push(new JoinRequestPlugin("after-described-resolve", "relative")); }
// raw-module
moduleExtensions.forEach(item => { plugins.push(new ModuleAppendPlugin("raw-module", item, "module")); }); if (!enforceModuleExtension) plugins.push(new TryNextPlugin("raw-module", null, "module"));
// module
modules.forEach(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
if (enableConcord) { plugins.push(new ConcordMainPlugin("existing-directory", {}, "resolve")); } mainFields.forEach(item => { plugins.push(new MainFieldPlugin("existing-directory", item, "resolve")); }); mainFiles.forEach(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")); } if (enableConcord) { plugins.push(new ConcordExtensionsPlugin("raw-file", {}, "file")); } extensions.forEach(item => { plugins.push(new AppendPlugin("raw-file", item, "file")); });
// file
if (alias.length > 0) plugins.push(new AliasPlugin("file", alias, "resolve")); if (enableConcord) { plugins.push(new ConcordModulesPlugin("file", {}, "resolve")); } aliasFields.forEach(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
if (restrictions.length > 0) { plugins.push(new RestrictionsPlugin(resolver.hooks.resolved, restrictions)); } plugins.push(new ResultPlugin(resolver.hooks.resolved));
//// RESOLVER ////
plugins.forEach(plugin => { plugin.apply(resolver); });
return resolver; };
function mergeFilteredToArray(array, filter) { return array.reduce((array, item) => { if (filter(item)) { const 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); }
|