import path from 'path' ; import { loaders } from './loaders' ; import { getPropertyByPath } from './getPropertyByPath' ; import { CosmiconfigResult , ExplorerOptions , ExplorerOptionsSync , Cache , LoadedFileContent , } from './types' ; import { Loader } from './index' ; class ExplorerBase < T extends ExplorerOptions | ExplorerOptionsSync > { protected readonly loadCache ? : Cache ; protected readonly searchCache ? : Cache ; protected readonly config : T ; public constructor ( options ) { if ( options . cache === true ) { this . loadCache = new Map ( ) ; this . searchCache = new Map ( ) ; } this . config = options ; this . validateConfig ( ) ; } public clearLoadCache ( ) { if ( this . loadCache ) { this . loadCache . clear ( ) ; } } public clearSearchCache ( ) { if ( this . searchCache ) { this . searchCache . clear ( ) ; } } public clearCaches ( ) { this . clearLoadCache ( ) ; this . clearSearchCache ( ) ; } private validateConfig ( ) { const config = this . config ; config . searchPlaces . forEach ( ( place ) : void => { 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` ) ; } if ( typeof loader !== 'function' ) { throw new Error ( `loader for ${ getExtensionDescription ( place ) } is not a function (type provided: "${ typeof loader }"), so searchPlaces item "${ place }" is invalid` ) ; } } ) ; } protected shouldSearchStopWithResult ( result ) { if ( result === null ) return false ; if ( result . isEmpty && this . config . ignoreEmptySearchPlaces ) return false ; return true ; } protected nextDirectoryToSearch ( currentDir , currentResult ) { if ( this . shouldSearchStopWithResult ( currentResult ) ) { return null ; } const nextDir = nextDirUp ( currentDir ) ; if ( nextDir === currentDir || currentDir === this . config . stopDir ) { return null ; } return nextDir ; } private loadPackageProp ( filepath , content ) { const parsedContent = loaders . loadJson ( filepath , content ) ; const packagePropValue = getPropertyByPath ( parsedContent , this . config . packageProp ) ; return packagePropValue || null ; } protected getLoaderEntryForFile ( filepath ) { if ( path . basename ( filepath ) === 'package.json' ) { const loader = this . loadPackageProp . bind ( this ) ; return loader ; } const loaderKey = path . extname ( filepath ) || 'noExt' ; const loader = this . config . loaders [ loaderKey ] ; if ( ! loader ) { throw new Error ( `No loader specified for ${ getExtensionDescription ( filepath ) }` ) ; } return loader ; } protected loadedContentToCosmiconfigResult ( filepath , loadedContent ) { if ( loadedContent === null ) { return null ; } if ( loadedContent === undefined ) { return { filepath , config : undefined , isEmpty : true } ; } return { config : loadedContent , filepath } ; } protected validateFilePath ( filepath ) { if ( ! filepath ) { throw new Error ( 'load must pass a non-empty string' ) ; } } } function nextDirUp ( dir ) { return path . dirname ( dir ) ; } function getExtensionDescription ( filepath ) { const ext = path . extname ( filepath ) ; return ext ? `extension "${ ext }"` : 'files without extensions' ; } export { ExplorerBase , getExtensionDescription } ;