| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 | 
							- //      
 
- 'use strict';
 
- const path = require('path');
 
- const loaders = require('./loaders');
 
- const readFile = require('./readFile');
 
- const cacheWrapper = require('./cacheWrapper');
 
- const getDirectory = require('./getDirectory');
 
- const getPropertyByPath = require('./getPropertyByPath');
 
- const MODE_SYNC = 'sync';
 
- // An object value represents a config object.
 
- // null represents that the loader did not find anything relevant.
 
- // undefined represents that the loader found something relevant
 
- // but it was empty.
 
-                                               
 
- class Explorer {
 
-                                                       
 
-                                                  
 
-                                                         
 
-                                                    
 
-                           
 
-   constructor(options                 ) {
 
-     this.loadCache = options.cache ? new Map() : null;
 
-     this.loadSyncCache = options.cache ? new Map() : null;
 
-     this.searchCache = options.cache ? new Map() : null;
 
-     this.searchSyncCache = options.cache ? new Map() : null;
 
-     this.config = options;
 
-     this.validateConfig();
 
-   }
 
-   clearLoadCache() {
 
-     if (this.loadCache) {
 
-       this.loadCache.clear();
 
-     }
 
-     if (this.loadSyncCache) {
 
-       this.loadSyncCache.clear();
 
-     }
 
-   }
 
-   clearSearchCache() {
 
-     if (this.searchCache) {
 
-       this.searchCache.clear();
 
-     }
 
-     if (this.searchSyncCache) {
 
-       this.searchSyncCache.clear();
 
-     }
 
-   }
 
-   clearCaches() {
 
-     this.clearLoadCache();
 
-     this.clearSearchCache();
 
-   }
 
-   validateConfig() {
 
-     const config = this.config;
 
-     config.searchPlaces.forEach(place => {
 
-       const loaderKey = path.extname(place) || 'noExt';
 
-       const loader = config.loaders[loaderKey];
 
-       if (!loader) {
 
-         throw new Error(
 
-           `No loader specified for ${getExtensionDescription(
 
-             place
 
-           )}, so searchPlaces item "${place}" is invalid`
 
-         );
 
-       }
 
-     });
 
-   }
 
-   search(searchFrom         )                             {
 
-     searchFrom = searchFrom || process.cwd();
 
-     return getDirectory(searchFrom).then(dir => {
 
-       return this.searchFromDirectory(dir);
 
-     });
 
-   }
 
-   searchFromDirectory(dir        )                             {
 
-     const absoluteDir = path.resolve(process.cwd(), dir);
 
-     const run = () => {
 
-       return this.searchDirectory(absoluteDir).then(result => {
 
-         const nextDir = this.nextDirectoryToSearch(absoluteDir, result);
 
-         if (nextDir) {
 
-           return this.searchFromDirectory(nextDir);
 
-         }
 
-         return this.config.transform(result);
 
-       });
 
-     };
 
-     if (this.searchCache) {
 
-       return cacheWrapper(this.searchCache, absoluteDir, run);
 
-     }
 
-     return run();
 
-   }
 
-   searchSync(searchFrom         )                    {
 
-     searchFrom = searchFrom || process.cwd();
 
-     const dir = getDirectory.sync(searchFrom);
 
-     return this.searchFromDirectorySync(dir);
 
-   }
 
-   searchFromDirectorySync(dir        )                    {
 
-     const absoluteDir = path.resolve(process.cwd(), dir);
 
-     const run = () => {
 
-       const result = this.searchDirectorySync(absoluteDir);
 
-       const nextDir = this.nextDirectoryToSearch(absoluteDir, result);
 
-       if (nextDir) {
 
-         return this.searchFromDirectorySync(nextDir);
 
-       }
 
-       return this.config.transform(result);
 
-     };
 
-     if (this.searchSyncCache) {
 
-       return cacheWrapper(this.searchSyncCache, absoluteDir, run);
 
-     }
 
-     return run();
 
-   }
 
-   searchDirectory(dir        )                             {
 
-     return this.config.searchPlaces.reduce((prevResultPromise, place) => {
 
-       return prevResultPromise.then(prevResult => {
 
-         if (this.shouldSearchStopWithResult(prevResult)) {
 
-           return prevResult;
 
-         }
 
-         return this.loadSearchPlace(dir, place);
 
-       });
 
-     }, Promise.resolve(null));
 
-   }
 
-   searchDirectorySync(dir        )                    {
 
-     let result = null;
 
-     for (const place of this.config.searchPlaces) {
 
-       result = this.loadSearchPlaceSync(dir, place);
 
-       if (this.shouldSearchStopWithResult(result)) break;
 
-     }
 
-     return result;
 
-   }
 
-   shouldSearchStopWithResult(result                   )          {
 
-     if (result === null) return false;
 
-     if (result.isEmpty && this.config.ignoreEmptySearchPlaces) return false;
 
-     return true;
 
-   }
 
-   loadSearchPlace(dir        , place        )                             {
 
-     const filepath = path.join(dir, place);
 
-     return readFile(filepath).then(content => {
 
-       return this.createCosmiconfigResult(filepath, content);
 
-     });
 
-   }
 
-   loadSearchPlaceSync(dir        , place        )                    {
 
-     const filepath = path.join(dir, place);
 
-     const content = readFile.sync(filepath);
 
-     return this.createCosmiconfigResultSync(filepath, content);
 
-   }
 
-   nextDirectoryToSearch(
 
-     currentDir        ,
 
-     currentResult                   
 
-   )          {
 
-     if (this.shouldSearchStopWithResult(currentResult)) {
 
-       return null;
 
-     }
 
-     const nextDir = nextDirUp(currentDir);
 
-     if (nextDir === currentDir || currentDir === this.config.stopDir) {
 
-       return null;
 
-     }
 
-     return nextDir;
 
-   }
 
-   loadPackageProp(filepath        , content        ) {
 
-     const parsedContent = loaders.loadJson(filepath, content);
 
-     const packagePropValue = getPropertyByPath(
 
-       parsedContent,
 
-       this.config.packageProp
 
-     );
 
-     return packagePropValue || null;
 
-   }
 
-   getLoaderEntryForFile(filepath        )              {
 
-     if (path.basename(filepath) === 'package.json') {
 
-       const loader = this.loadPackageProp.bind(this);
 
-       return { sync: loader, async: loader };
 
-     }
 
-     const loaderKey = path.extname(filepath) || 'noExt';
 
-     return this.config.loaders[loaderKey] || {};
 
-   }
 
-   getSyncLoaderForFile(filepath        )             {
 
-     const entry = this.getLoaderEntryForFile(filepath);
 
-     if (!entry.sync) {
 
-       throw new Error(
 
-         `No sync loader specified for ${getExtensionDescription(filepath)}`
 
-       );
 
-     }
 
-     return entry.sync;
 
-   }
 
-   getAsyncLoaderForFile(filepath        )              {
 
-     const entry = this.getLoaderEntryForFile(filepath);
 
-     const loader = entry.async || entry.sync;
 
-     if (!loader) {
 
-       throw new Error(
 
-         `No async loader specified for ${getExtensionDescription(filepath)}`
 
-       );
 
-     }
 
-     return loader;
 
-   }
 
-   loadFileContent(
 
-     mode                  ,
 
-     filepath        ,
 
-     content               
 
-   )                                                 {
 
-     if (content === null) {
 
-       return null;
 
-     }
 
-     if (content.trim() === '') {
 
-       return undefined;
 
-     }
 
-     const loader =
 
-       mode === MODE_SYNC
 
-         ? this.getSyncLoaderForFile(filepath)
 
-         : this.getAsyncLoaderForFile(filepath);
 
-     return loader(filepath, content);
 
-   }
 
-   loadedContentToCosmiconfigResult(
 
-     filepath        ,
 
-     loadedContent                   
 
-   )                    {
 
-     if (loadedContent === null) {
 
-       return null;
 
-     }
 
-     if (loadedContent === undefined) {
 
-       return { filepath, config: undefined, isEmpty: true };
 
-     }
 
-     return { config: loadedContent, filepath };
 
-   }
 
-   createCosmiconfigResult(
 
-     filepath        ,
 
-     content               
 
-   )                             {
 
-     return Promise.resolve()
 
-       .then(() => {
 
-         return this.loadFileContent('async', filepath, content);
 
-       })
 
-       .then(loaderResult => {
 
-         return this.loadedContentToCosmiconfigResult(filepath, loaderResult);
 
-       });
 
-   }
 
-   createCosmiconfigResultSync(
 
-     filepath        ,
 
-     content               
 
-   )                    {
 
-     const loaderResult = this.loadFileContent('sync', filepath, content);
 
-     return this.loadedContentToCosmiconfigResult(filepath, loaderResult);
 
-   }
 
-   validateFilePath(filepath         ) {
 
-     if (!filepath) {
 
-       throw new Error('load and loadSync must pass a non-empty string');
 
-     }
 
-   }
 
-   load(filepath        )                             {
 
-     return Promise.resolve().then(() => {
 
-       this.validateFilePath(filepath);
 
-       const absoluteFilePath = path.resolve(process.cwd(), filepath);
 
-       return cacheWrapper(this.loadCache, absoluteFilePath, () => {
 
-         return readFile(absoluteFilePath, { throwNotFound: true })
 
-           .then(content => {
 
-             return this.createCosmiconfigResult(absoluteFilePath, content);
 
-           })
 
-           .then(this.config.transform);
 
-       });
 
-     });
 
-   }
 
-   loadSync(filepath        )                    {
 
-     this.validateFilePath(filepath);
 
-     const absoluteFilePath = path.resolve(process.cwd(), filepath);
 
-     return cacheWrapper(this.loadSyncCache, absoluteFilePath, () => {
 
-       const content = readFile.sync(absoluteFilePath, { throwNotFound: true });
 
-       const result = this.createCosmiconfigResultSync(
 
-         absoluteFilePath,
 
-         content
 
-       );
 
-       return this.config.transform(result);
 
-     });
 
-   }
 
- }
 
- module.exports = function createExplorer(options                 ) {
 
-   const explorer = new Explorer(options);
 
-   return {
 
-     search: explorer.search.bind(explorer),
 
-     searchSync: explorer.searchSync.bind(explorer),
 
-     load: explorer.load.bind(explorer),
 
-     loadSync: explorer.loadSync.bind(explorer),
 
-     clearLoadCache: explorer.clearLoadCache.bind(explorer),
 
-     clearSearchCache: explorer.clearSearchCache.bind(explorer),
 
-     clearCaches: explorer.clearCaches.bind(explorer),
 
-   };
 
- };
 
- function nextDirUp(dir        )         {
 
-   return path.dirname(dir);
 
- }
 
- function getExtensionDescription(filepath        )         {
 
-   const ext = path.extname(filepath);
 
-   return ext ? `extension "${ext}"` : 'files without extensions';
 
- }
 
 
  |