Socket
Socket
Sign inDemoInstall

cosmiconfig

Package Overview
Dependencies
9
Maintainers
2
Versions
56
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 4.0.0 to 5.0.1

dist/cacheWrapper.js

34

CHANGELOG.md
# Changelog
## 5.0.1
The API has been completely revamped to increase clarity and enable a very wide range of new usage. **Please read the readme for all the details.**
While the defaults remain just as useful as before — and you can still pass no options at all — now you can also do all kinds of wild and crazy things.
- The `loaders` option allows you specify custom functions to derive config objects from files. Your loader functions could parse ES2015 modules or TypeScript, JSON5, even INI or XML. Whatever suits you.
- The `searchPlaces` option allows you to specify exactly where cosmiconfig looks within each directory it searches.
- The combination of `loaders` and `searchPlaces` means that you should be able to load pretty much any kind of configuration file you want, from wherever you want it to look.
Additionally, the overloaded `load()` function has been split up into several clear and focused functions:
- `search()` now searches up the directory tree, and `load()` loads a configuration file that you don't need to search for.
- The `sync` option has been replaced with separate synchronous functions: `searchSync()` and `loadSync()`.
- `clearFileCache()` and `clearDirectoryCache()` have been renamed to `clearLoadCache()` and `clearSearchPath()` respectively.
More details:
- The default JS loader uses `require`, instead of `require-from-string`. So you *could* use `require` hooks to control the loading of JS files (e.g. pass them through esm or Babel). In most cases it is probably preferable to use a custom loader.
- The options `rc`, `js`, and `rcExtensions` have all been removed. You can accomplish the same and more with `searchPlaces`.
- The default `searchPlaces` include `rc` files with extensions, e.g. `.thingrc.json`, `.thingrc.yaml`, `.thingrc.yml`. This is the equivalent of switching the default value of the old `rcExtensions` option to `true`.
- The option `rcStrictJson` has been removed. To get the same effect, you can specify `noExt: cosmiconfig.loadJson` in your `loaders` object.
- `packageProp` no longer accepts `false`. If you don't want to look in `package.json`, write a `searchPlaces` array that does not include it.
- By default, empty files are ignored by `search()`. The new option `ignoreEmptySearchPlaces` allows you to load them, instead, in case you want to do something with empty files.
- The option `configPath` has been removed. Just pass your filepaths directory to `load()`.
- Removed the `format` option. Formats are now all handled via the file extensions specified in `loaders`.
(If you're wondering with happened to 5.0.0 ... it was a silly publishing mistake.)
## 4.0.0

@@ -22,3 +51,3 @@

- Removed: `argv` config option.
- Removed: support for Node versions < 4.
- Removed: support for Node versions &lt; 4.
- Added: `sync` option.

@@ -65,3 +94,3 @@ - Fixed: Throw a clear error on getting empty config file.

- Added: caching (enabled by the change above).
- Removed: support for Node versions <4.
- Removed: support for Node versions &lt;4.

@@ -85,2 +114,3 @@ ## 1.1.0

[parse-json-pr-12]: https://github.com/sindresorhus/parse-json/pull/12
[pr-101]: https://github.com/davidtheclark/cosmiconfig/pull/101

406

dist/createExplorer.js

@@ -5,156 +5,322 @@ //

const path = require('path');
const loadPackageProp = require('./loadPackageProp');
const loadRc = require('./loadRc');
const loadJs = require('./loadJs');
const loadDefinedFile = require('./loadDefinedFile');
const funcRunner = require('./funcRunner');
const loaders = require('./loaders');
const readFile = require('./readFile');
const cacheWrapper = require('./cacheWrapper');
const getDirectory = require('./getDirectory');
module.exports = function createExplorer(options
) {
// When `options.sync` is `false` (default),
// these cache Promises that resolve with results, not the results themselves.
const fileCache = options.cache ? new Map() : null;
const directoryCache = options.cache ? new Map() : null;
const transform = options.transform || identity;
const packageProp = options.packageProp;
const MODE_SYNC = 'sync';
function clearFileCache() {
if (fileCache) fileCache.clear();
}
// 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.
function clearDirectoryCache() {
if (directoryCache) directoryCache.clear();
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();
}
function clearCaches() {
clearFileCache();
clearDirectoryCache();
clearLoadCache() {
if (this.loadCache) {
this.loadCache.clear();
}
if (this.loadSyncCache) {
this.loadSyncCache.clear();
}
}
function throwError(error) {
if (options.sync) {
throw error;
} else {
return Promise.reject(error);
clearSearchCache() {
if (this.searchCache) {
this.searchCache.clear();
}
if (this.searchSyncCache) {
this.searchSyncCache.clear();
}
}
function load(
searchPath ,
configPath
) {
if (!searchPath) searchPath = process.cwd();
if (!configPath && options.configPath) configPath = options.configPath;
clearCaches() {
this.clearLoadCache();
this.clearSearchCache();
}
if (configPath) {
const absoluteConfigPath = path.resolve(process.cwd(), configPath);
if (fileCache && fileCache.has(absoluteConfigPath)) {
return fileCache.get(absoluteConfigPath);
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`
);
}
});
}
let load;
if (path.basename(absoluteConfigPath) === 'package.json') {
if (!packageProp) {
return throwError(
new Error(
'Please specify the packageProp option. The configPath argument cannot point to a package.json file if packageProp is false.'
)
);
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);
}
load = () =>
loadPackageProp(path.dirname(absoluteConfigPath), {
packageProp,
sync: options.sync,
});
} else {
load = () =>
loadDefinedFile(absoluteConfigPath, {
sync: options.sync,
format: options.format,
});
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);
};
const loadResult = load();
const result =
loadResult instanceof Promise
? loadResult.then(transform)
: transform(loadResult);
if (fileCache) fileCache.set(absoluteConfigPath, result);
return result;
if (this.searchSyncCache) {
return cacheWrapper(this.searchSyncCache, absoluteDir, run);
}
return run();
}
const absoluteSearchPath = path.resolve(process.cwd(), searchPath);
const searchPathDir = getDirectory(absoluteSearchPath, options.sync);
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));
}
return searchPathDir instanceof Promise
? searchPathDir.then(searchDirectory)
: searchDirectory(searchPathDir);
searchDirectorySync(dir ) {
let result = null;
for (const place of this.config.searchPlaces) {
result = this.loadSearchPlaceSync(dir, place);
if (this.shouldSearchStopWithResult(result)) break;
}
return result;
}
function searchDirectory(
directory
) {
if (directoryCache && directoryCache.has(directory)) {
return directoryCache.get(directory);
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;
}
const result = funcRunner(!options.sync ? Promise.resolve() : undefined, [
() => {
if (!packageProp) return;
return loadPackageProp(directory, {
packageProp,
sync: options.sync,
});
},
result => {
if (result || !options.rc) return result;
return loadRc(path.join(directory, options.rc), {
sync: options.sync,
rcStrictJson: options.rcStrictJson,
rcExtensions: options.rcExtensions,
});
},
result => {
if (result || !options.js) return result;
return loadJs(path.join(directory, options.js), { sync: options.sync });
},
result => {
if (result) return result;
loadPackageProp(filepath , content ) {
const parsedContent = loaders.loadJson(filepath, content);
const packagePropValue = parsedContent[this.config.packageProp];
return packagePropValue || null;
}
const nextDirectory = path.dirname(directory);
getLoaderEntryForFile(filepath ) {
if (path.basename(filepath) === 'package.json') {
const loader = this.loadPackageProp.bind(this);
return { sync: loader, async: loader };
}
if (nextDirectory === directory || directory === options.stopDir)
return null;
const loaderKey = path.extname(filepath) || 'noExt';
return this.config.loaders[loaderKey];
}
return searchDirectory(nextDirectory);
},
transform,
]);
getSyncLoaderForFile(filepath ) {
const entry = this.getLoaderEntryForFile(filepath);
if (!entry.sync) {
throw new Error(
`No sync loader specified for ${getExtensionDescription(filepath)}`
);
}
return entry.sync;
}
if (directoryCache) directoryCache.set(directory, result);
return result;
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);
const loadedContent = loader(filepath, content);
if (mode === MODE_SYNC && loadedContent instanceof Promise) {
throw new Error(
`The sync loader for "${path.basename(
filepath
)}" returned a Promise. Sync loaders need to be synchronous.`
);
}
return loadedContent;
}
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 be 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(filepath, 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(filepath, content);
return this.config.transform(result);
});
}
}
module.exports = function createExplorer(options ) {
const explorer = new Explorer(options);
return {
load,
clearFileCache,
clearDirectoryCache,
clearCaches,
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 identity(x) {
return x;
function nextDirUp(dir ) {
return path.dirname(dir);
}
function getExtensionDescription(filepath ) {
const ext = path.extname(filepath);
return ext ? `extension "${ext}"` : 'files without extensions';
}

@@ -7,10 +7,3 @@ //

module.exports = function getDirectory(
filepath ,
sync
) {
if (sync === true) {
return isDirectory.sync(filepath) ? filepath : path.dirname(filepath);
}
function getDirectory(filepath ) {
return new Promise((resolve, reject) => {

@@ -24,2 +17,8 @@ return isDirectory(filepath, (err, filepathIsDirectory) => {

});
}
getDirectory.sync = function getDirectorySync(filepath ) {
return isDirectory.sync(filepath) ? filepath : path.dirname(filepath);
};
module.exports = getDirectory;

@@ -6,36 +6,76 @@ //

const createExplorer = require('./createExplorer');
const loaders = require('./loaders');
const homedir = os.homedir();
module.exports = cosmiconfig;
module.exports = function cosmiconfig(
function cosmiconfig(
moduleName ,
options
) {
options = Object.assign(
options = options || {};
const defaults = {
packageProp: moduleName,
searchPlaces: [
'package.json',
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`${moduleName}.config.js`,
],
ignoreEmptySearchPlaces: true,
stopDir: os.homedir(),
cache: true,
transform: identity,
};
const normalizedOptions = Object.assign(
{},
defaults,
options,
{
packageProp: moduleName,
rc: `.${moduleName}rc`,
js: `${moduleName}.config.js`,
rcStrictJson: false,
stopDir: homedir,
cache: true,
sync: false,
},
options
loaders: normalizeLoaders(options.loaders),
}
);
return createExplorer(options);
};
return createExplorer(normalizedOptions);
}
cosmiconfig.loadJs = loaders.loadJs;
cosmiconfig.loadJson = loaders.loadJson;
cosmiconfig.loadYaml = loaders.loadYaml;
function normalizeLoaders(rawLoaders ) {
const defaults = {
'.js': { sync: loaders.loadJs, async: loaders.loadJs },
'.json': { sync: loaders.loadJson, async: loaders.loadJson },
'.yaml': { sync: loaders.loadYaml, async: loaders.loadYaml },
'.yml': { sync: loaders.loadYaml, async: loaders.loadYaml },
noExt: { sync: loaders.loadYaml, async: loaders.loadYaml },
};
if (!rawLoaders) {
return defaults;
}
return Object.keys(rawLoaders).reduce((result, ext) => {
const entry = rawLoaders && rawLoaders[ext];
if (typeof entry === 'function') {
result[ext] = { sync: entry, async: entry };
} else {
result[ext] = entry;
}
return result;
}, defaults);
}
function identity(x) {
return x;
}

@@ -10,3 +10,3 @@ //

function readFile(filepath , options ) {
function readFile(filepath , options ) {
options = options || {};

@@ -29,3 +29,3 @@ const throwNotFound = options.throwNotFound || false;

options
) {
) {
options = options || {};

@@ -32,0 +32,0 @@ const throwNotFound = options.throwNotFound || false;

{
"name": "cosmiconfig",
"version": "4.0.0",
"version": "5.0.1",
"description": "Find and load configuration from a package.json property, rc file, or CommonJS module",

@@ -11,4 +11,6 @@ "main": "dist/index.js",

"precommit": "lint-staged && jest && flow check",
"lint": "eslint .",
"lint:md-partial": "remark -u remark-preset-davidtheclark --frail --quiet --no-stdout --output --",
"lint:md": "npm run lint:md-partial -- *.md",
"lint:fix": "eslint . --fix",
"lint": "eslint . && npm run lint:md",
"format": "prettier --write \"{src/*.js,test/*.js}\"",

@@ -27,2 +29,6 @@ "pretest": "npm run lint && flow check",

"git add"
],
"*.md": [
"npm run lint:md-partial",
"git add"
]

@@ -60,2 +66,6 @@ },

],
"coverageReporters": [
"text",
"html"
],
"coverageThreshold": {

@@ -68,3 +78,5 @@ "global": {

}
}
},
"resetModules": true,
"resetMocks": true
},

@@ -79,4 +91,3 @@ "babel": {

"js-yaml": "^3.9.0",
"parse-json": "^4.0.0",
"require-from-string": "^2.0.1"
"parse-json": "^4.0.0"
},

@@ -86,2 +97,3 @@ "devDependencies": {

"babel-plugin-transform-flow-strip-types": "^6.22.0",
"del": "^3.0.0",
"eslint": "^4.12.1",

@@ -92,3 +104,3 @@ "eslint-config-davidtheclark-node": "^0.2.2",

"eslint-plugin-node": "^5.2.1",
"flow-bin": "^0.54.1",
"flow-bin": "^0.68.0",
"flow-remove-types": "^1.2.3",

@@ -98,3 +110,7 @@ "husky": "^0.14.3",

"lint-staged": "^6.0.0",
"prettier": "^1.8.2"
"make-dir": "^1.2.0",
"parent-module": "^0.1.0",
"prettier": "^1.8.2",
"remark-cli": "^5.0.0",
"remark-preset-davidtheclark": "^0.7.0"
},

@@ -101,0 +117,0 @@ "engines": {

@@ -5,19 +5,56 @@ # cosmiconfig

Find and load a configuration object from
- a `package.json` property (anywhere up the directory tree)
- a JSON or YAML "rc file" (anywhere up the directory tree)
- a `.config.js` CommonJS module (anywhere up the directory tree)
Cosmiconfig searches for and loads configuration for your program.
For example, if your module's name is "soursocks," cosmiconfig will search out configuration in the following places:
It features smart defaults based on conventional expectations in the JavaScript ecosystem.
But it's also flexible enough to search wherever you'd like to search, and load whatever you'd like to load.
By default, Cosmiconfig will start where you tell it to start and search up the directory tree for the following:
- a `package.json` property
- a JSON or YAML, extensionless "rc file"
- an "rc file" with the extensions `.json`, `.yaml`, or `.yml`.
- a `.config.js` CommonJS module
For example, if your module's name is "soursocks", cosmiconfig will search for configuration in the following places:
- a `soursocks` property in `package.json` (anywhere up the directory tree)
- a `.soursocksrc` file in JSON or YAML format (anywhere up the directory tree)
- a `.soursocksrc.json` file
- a `.soursocksrc.yaml` or `.soursocksrc.yml` file
- a `soursocks.config.js` file exporting a JS object (anywhere up the directory tree)
cosmiconfig continues to search in these places all the way up the directory tree until it finds acceptable configuration (or hits the home directory).
Cosmiconfig continues to search up the directory tree, checking each of these places in each directory, until it finds some acceptable configuration (or hits the home directory).
Additionally, all of these search locations are configurable: you can customize filenames or turn off any location.
👀 **Looking for the v4 docs?**
v5 involves significant revisions to Cosmiconfig's API, allowing for much greater flexibility and clarifying some things.
If you have trouble switching from v4 to v5, please file an issue.
If you are still using v4, those v4 docs are available [in the `4.0.0` tag](https://github.com/davidtheclark/cosmiconfig/tree/4.0.0).
You can also look for rc files with extensions, e.g. `.soursocksrc.json` or `.soursocksrc.yaml`.
You may like extensions on your rc files because you'll get syntax highlighting and linting in text editors.
## Table of contents
- [Installation](#installation)
- [Usage](#usage)
- [Result](#result)
- [cosmiconfig()](#cosmiconfig-1)
- [moduleName](#modulename)
- [explorer.search()](#explorersearch)
- [searchFrom](#searchfrom)
- [explorer.searchSync()](#explorersearchsync)
- [explorer.load()](#explorerload)
- [explorer.loadSync()](#explorerloadsync)
- [explorer.clearLoadCache()](#explorerclearloadcache)
- [explorer.clearSearchCache()](#explorerclearsearchcache)
- [explorer.clearCaches()](#explorerclearcaches)
- [cosmiconfigOptions](#cosmiconfigoptions)
- [searchPlaces](#searchplaces)
- [loaders](#loaders)
- [packageProp](#packageprop)
- [stopDir](#stopdir)
- [cache](#cache)
- [transform](#transform)
- [ignoreEmptySearchPlaces](#ignoreemptysearchplaces)
- [Caching](#caching)
- [Differences from rc](#differences-from-rc)
- [Contributing & Development](#contributing--development)
## Installation

@@ -33,199 +70,382 @@

Create a Cosmiconfig explorer, then either `search` for or directly `load` a configuration file.
```js
var cosmiconfig = require('cosmiconfig');
const cosmiconfig = require('cosmiconfig');
// ...
const explorer = cosmiconfig(moduleName);
var explorer = cosmiconfig(yourModuleName[, options]);
explorer.load()
// Search for a configuration by walking up directories.
// See documentation for search, below.
explorer.search()
.then((result) => {
// result.config is the parsed configuration object
// result.filepath is the path to the config file that was found
// result.config is the parsed configuration object.
// result.filepath is the path to the config file that was found.
// result.isEmpty is true if there was nothing to parse in the config file.
})
.catch((parsingError) => {
// do something constructive
.catch((error) => {
// Do something constructive.
});
// Load a configuration directly when you know where it should be.
// The result object is the same as for search.
// See documentation for load, below.
explorer.load(pathToConfig).then(..);
// You can also search and load synchronously.
const searchedFor = explorer.searchSync();
const loaded = explorer.loadSync(pathToConfig);
```
The function `cosmiconfig()` searches for a configuration object and returns a Promise,
which resolves with an object containing the information you're looking for.
## Result
You can also pass option `sync: true` to load the config synchronously, returning the config itself.
The result object you get from `search` or `load` has the following properties:
So let's say `var yourModuleName = 'goldengrahams'` — here's how cosmiconfig will work:
- **config:** The parsed configuration object. `undefined` if the file is empty.
- **filepath:** The path to the configuration file that was found.
- **isEmpty:** `true` if the configuration file is empty. This property will not be present if the configuration file is not empty.
- Starting from `process.cwd()` (or some other directory defined by the `searchPath` argument to `load()`), it looks for configuration objects in three places, in this order:
1. A `goldengrahams` property in a `package.json` file (or some other property defined by `options.packageProp`);
2. A `.goldengrahamsrc` file with JSON or YAML syntax (or some other filename defined by `options.rc`);
3. A `goldengrahams.config.js` JS file exporting the object (or some other filename defined by `options.js`).
- If none of those searches reveal a configuration object, it moves up one directory level and tries again. So the search continues in `./`, `../`, `../../`, `../../../`, etc., checking those three locations in each directory.
- It continues searching until it arrives at your home directory (or some other directory defined by `options.stopDir`).
- If at any point a parseable configuration is found, the `cosmiconfig()` Promise resolves with its result object.
- If no configuration object is found, the `cosmiconfig()` Promise resolves with `null`.
- If a configuration object is found *but is malformed* (causing a parsing error), the `cosmiconfig()` Promise rejects and shares that error (so you should `.catch()` it).
## cosmiconfig()
All this searching can be short-circuited by passing `options.configPath` to specify a file.
cosmiconfig will read that file and try parsing it as JSON, YAML, or JS.
```js
const explorer = cosmiconfig(moduleName[, cosmiconfigOptions])
```
## Caching
Creates a cosmiconfig instance ("explorer") configured according to the arguments, and initializes its caches.
As of v2, cosmiconfig uses a few caches to reduce the need for repetitious reading of the filesystem. Every new cosmiconfig instance (created with `cosmiconfig()`) has its own caches.
### moduleName
To avoid or work around caching, you can
- create separate instances of cosmiconfig, or
- set `cache: false` in your options.
- use the cache clearing methods documented below.
Type: `string`. **Required.**
## API
Your module name. This is used to create the default [`searchPlaces`] and [`packageProp`].
### `var explorer = cosmiconfig(moduleName[, options])`
**[`cosmiconfigOptions`] are documented below.**
You may not need them, and should first read about the functions you'll use.
Creates a cosmiconfig instance (i.e. explorer) configured according to the arguments, and initializes its caches.
## explorer.search()
#### moduleName
```js
explorer.search([searchFrom]).then(result => {..})
```
Type: `string`
Searches for a configuration file. Returns a Promise that resolves with a [result] or with `null`, if no configuration file is found.
You module name. This is used to create the default filenames that cosmiconfig will look for.
You can do the same thing synchronously with [`searchSync()`].
#### Options
Let's say your module name is `goldengrahams` so you initialized with `const explorer = cosmiconfig('goldengrahams');`.
Here's how your default [`search()`] will work:
##### packageProp
- Starting from `process.cwd()` (or some other directory defined by the `searchFrom` argument to [`search()`]), look for configuration objects in the following places:
1. A `goldengrahams` property in a `package.json` file.
2. A `.goldengrahamsrc` file with JSON or YAML syntax.
3. A `.goldengrahamsrc.json` file.
4. A `.goldengrahamsrc.yaml` or `.goldengrahamsrc.yml` file.
5. A `goldengrahams.config.js` JS file exporting the object.
- If none of those searches reveal a configuration object, move up one directory level and try again.
So the search continues in `./`, `../`, `../../`, `../../../`, etc., checking the same places in each directory.
- Continue searching until arriving at your home directory (or some other directory defined by the cosmiconfig option [`stopDir`]).
- If at any point a parseable configuration is found, the [`search()`] Promise resolves with its [result] \(or, with [`searchSync()`], the [result] is returned).
- If no configuration object is found, the [`search()`] Promise resolves with `null` (or, with [`searchSync()`], `null` is returned).
- If a configuration object is found *but is malformed* (causing a parsing error), the [`search()`] Promise rejects with that error (so you should `.catch()` it). (Or, with [`searchSync()`], the error is thrown.)
Type: `string` or `false`
Default: `'[moduleName]'`
**If you know exactly where your configuration file should be, you can use [`load()`], instead.**
Name of the property in `package.json` to look for.
**The search process is highly customizable.**
Use the cosmiconfig options [`searchPlaces`] and [`loaders`] to precisely define where you want to look for configurations and how you want to load them.
If `false`, cosmiconfig will not look in `package.json` files.
### searchFrom
##### rc
Type: `string`.
Default: `process.cwd()`.
Type: `string` or `false`
Default: `'.[moduleName]rc'`
A filename.
[`search()`] will start its search here.
Name of the "rc file" to look for, which can be formatted as JSON or YAML.
If the value is a directory, that's where the search starts.
If it's a file, the search starts in that file's directory.
If `false`, cosmiconfig will not look for an rc file.
## explorer.searchSync()
If `rcExtensions: true`, the rc file can also have extensions that specify the syntax, e.g. `.[moduleName]rc.json`.
You may like extensions on your rc files because you'll get syntax highlighting and linting in text editors.
Also, with `rcExtensions: true`, you can use JS modules as rc files, e.g. `.[moduleName]rc.js`.
```js
const result = explorer.search([searchFrom]);
```
##### js
Synchronous version of [`search()`].
Type: `string` or `false`
Default: `'[moduleName].config.js'`
Returns a [result] or `null`.
Name of a JS file to look for, which must export the configuration object.
## explorer.load()
If `false`, cosmiconfig will not look for a JS file.
```js
explorer.load([loadPath]).then(result => {..})
```
##### rcStrictJson
Loads a configuration file. Returns a Promise that resolves with a [result] or rejects with an error (if the file does not exist or cannot be loaded).
Type: `boolean`
Default: `false`
Use `load` if you already know where the configuration file is and you just need to load it.
If `true`, cosmiconfig will expect rc files to be strict JSON. No YAML permitted, and no sloppy JSON.
```js
explorer.load('load/this/file.json'); // Tries to load load/this/file.json.
```
By default, rc files are parsed with [js-yaml](https://github.com/nodeca/js-yaml), which is
more permissive with punctuation than standard strict JSON.
If you load a `package.json` file, the result will be derived from whatever property is specified as your [`packageProp`].
##### rcExtensions
## explorer.loadSync()
Type: `boolean`
Default: `false`
```js
const result = explorer.load([loadPath]);
```
If `true`, cosmiconfig will look for rc files with extensions, in addition to rc files without.
Synchronous version of [`load()`].
This adds a few steps to the search process.
Instead of *just* looking for `.goldengrahamsrc` (no extension), it will also look for the following, in this order:
Returns a [result].
- `.goldengrahamsrc.json`
- `.goldengrahamsrc.yaml`
- `.goldengrahamsrc.yml`
- `.goldengrahamsrc.js`
## explorer.clearLoadCache()
##### stopDir
Clears the cache used in [`load()`].
Type: `string`
Default: Absolute path to your home directory
## explorer.clearSearchCache()
Directory where the search will stop.
Clears the cache used in [`search()`].
##### cache
## explorer.clearCaches()
Type: `boolean`
Default: `true`
Performs both [`clearLoadCache()`] and [`clearSearchCache()`].
If `false`, no caches will be used.
## cosmiconfigOptions
##### sync
### searchPlaces
Type: `boolean`
Default: `false`
Type: `Array<string>`.
Default: See below.
If `true`, config will be loaded synchronously.
An array of places that [`search()`] will check in each directory as it moves up the directory tree.
Each place is relative to the directory being searched, and the places are checked in the specified order.
##### transform
**Default `searchPlaces`:**
Type: `Function`
```js
[
'package.json',
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`${moduleName}.config.js`,
]
```
A function that transforms the parsed configuration. Receives the result object with `config` and `filepath` properties.
Create your own array to search more, fewer, or altogether different places.
If the option `sync` is `false` (default), the function must return a Promise that resolves with the transformed result.
If the option `sync` is `true`, though, `transform` should be a synchronous function which returns the transformed result.
Every item in `searchPlaces` needs to have a loader in [`loaders`] that corresponds to its extension.
(Common extensions are covered by default loaders.)
Read more about [`loaders`] below.
The reason you might use this option instead of simply applying your transform function some other way is that *the transformed result will be cached*. If your transformation involves additional filesystem I/O or other potentially slow processing, you can use this option to avoid repeating those steps every time a given configuration is loaded.
`package.json` is a special value: When it is included in `searchPlaces`, Cosmiconfig will always parse it as JSON and load a property within it, not the whole file.
That property is defined with the [`packageProp`] option, and defaults to your module name.
##### configPath
Examples, with a module named `porgy`:
Type: `string`
```js
// Disallow extensions on rc files:
[
'package.json',
'.porgyrc',
'porgy.config.js'
]
If provided, cosmiconfig will load and parse a config from this path, and will not perform its usual search.
// ESLint searches for configuration in these places:
[
'.eslintrc.js',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
'.eslintrc',
'package.json'
]
##### format
// Babel looks in fewer places:
[
'package.json',
'.babelrc'
]
Type: `'json' | 'yaml' | 'js'`
// Maybe you want to look for a wide variety of JS flavors:
[
'porgy.config.js',
'porgy.config.mjs',
'porgy.config.ts',
'porgy.config.coffee'
]
// ^^ You will need to designate custom loaders to tell
// Cosmiconfig how to handle these special JS flavors.
The expected file format for the config loaded from `configPath`.
// Look within a .config/ subdirectory of every searched directory:
[
'package.json',
'.porgyrc',
'.config/.porgyrc',
'.porgyrc.json',
'.config/.porgyrc.json'
]
```
If not specified, cosmiconfig will try to infer the format using the extension name (if it has one).
In the event that the file does not have an extension or the extension is unrecognized, cosmiconfig will try to parse it as a JSON, YAML, or JS file.
### loaders
### Instance methods (on `explorer`)
Type: `Object`.
Default: See below.
#### `load([searchPath, configPath])`
An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions.
Find and load a configuration file. Returns a Promise that resolves with `null`, if nothing is found, or an object with two properties:
- `config`: The loaded and parsed configuration.
- `filepath`: The filepath where this configuration was found.
Cosmiconfig exposes its default loaders for `.js`, `.json`, and `.yaml` as `cosmiconfig.loadJs`, `cosmiconfig.loadJson`, and `cosmiconfig.loadYaml`, respectively.
You should provide *either* `searchPath` *or* `configPath`. Use `configPath` if you know the path of the configuration file you want to load. Note that `configPath` takes priority over `searchPath` if both parameters are specified.
**Default `loaders`:**
```js
explorer.load()
{
'.json': cosmiconfig.loadJson,
'.yaml': cosmiconfig.loadYaml,
'.yml': cosmiconfig.loadYaml,
'.js': cosmiconfig.loadJs,
noExt: cosmiconfig.loadYaml
}
```
explorer.load('start/search/here');
explorer.load('start/search/at/this/file.css');
(YAML is a superset of JSON; which means YAML parsers can parse JSON; which is how extensionless files can be either YAML *or* JSON with only one parser.)
explorer.load(null, 'load/this/file.json');
**If you provide a `loaders` object, your object will be *merged* with the defaults.**
So you can override one or two without having to override them all.
**Keys in `loaders`** are extensions (starting with a period), or `noExt` to specify the loader for files *without* extensions, like `.soursocksrc`.
**Values in `loaders`** are either a loader function (described below) or an object with `sync` and/or `async` properties, whose values are loader functions.
**The most common use case for custom loaders value is to load extensionless `rc` files as strict JSON**, instead of JSON *or* YAML (the default).
To accomplish that, provide the following `loaders` value:
```js
{
noExt: cosmiconfig.loadJson
}
```
If you provide `searchPath`, cosmiconfig will start its search at `searchPath` and continue to search up the directory tree, as documented above.
By default, `searchPath` is set to `process.cwd()`.
If you want to load files that are not handled by the loader functions Cosmiconfig exposes, you can write a custom loader function.
If you provide `configPath` (i.e. you already know where the configuration is that you want to load), cosmiconfig will try to read and parse that file. Note that the [`format` option](#format) is applicable for this as well.
**Use cases for custom loader function:**
#### `clearFileCache()`
- Allow configuration syntaxes that aren't handled by Cosmiconfig's defaults, like JSON5, INI, or XML.
- Allow ES2015 modules from `.mjs` configuration files.
- Parse JS files with Babel before deriving the configuration.
Clears the cache used when you provide a `configPath` argument to `load`.
**Custom loader functions** have the following signature:
#### `clearDirectoryCache()`
```js
// Sync
(filepath: string, content: string) => Object | null
Clears the cache used when you provide a `searchPath` argument to `load`.
// Async
(filepath: string, content: string) => Object | null | Promise<Object | null>
```
#### `clearCaches()`
Cosmiconfig reads the file when it checks whether the file exists, so it will provide you with both the file's path and its content.
Do whatever you need to, and return either a configuration object or `null` (or, for async-only loaders, a Promise that resolves with one of those).
`null` indicates that no real configuration was found and the search should continue.
Performs both `clearFileCache()` and `clearDirectoryCache()`.
It's easiest if you make your custom loader function synchronous.
Then it can be used regardless of whether you end up calling [`search()`] or [`searchSync()`], [`load()`] or [`loadSync()`].
If you want or need to provide an async-only loader, you can do so by making the value of `loaders` an object with an `async` property whose value is the async loader.
You can also add a `sync` property to designate a sync loader, if you want to use both async and sync search and load functions.
If an extension has *only* an async loader but you try to use [`searchSync()`] or [`loadSync()`], an error will be thrown.
Note that **special JS syntax can also be handled by using a `require` hook**, because `cosmiconfig.loadJs` just uses `require`.
Whether you use custom loaders or a `require` hook is up to you.
Examples:
```js
// Allow JSON5 syntax:
{
'.json': json5Loader
}
// Allow XML, and treat sync and async separately:
{
'.xml': { async: asyncXmlLoader, sync: syncXmlLoader }
}
// Allow a special configuration syntax of your own creation:
{
'.special': specialLoader
}
// Allow many flavors of JS, using custom loaders:
{
'.mjs': esmLoader,
'.ts': typeScriptLoader,
'.coffee': coffeeScriptLoader
}
// Allow many flavors of JS but rely on require hooks:
{
'.mjs': cosmiconfig.loadJs,
'.ts': cosmiconfig.loadJs,
'.coffee': cosmiconfig.loadJs
}
```
### packageProp
Type: `string`.
Default: `` `${moduleName}` ``.
Name of the property in `package.json` to look for.
### stopDir
Type: `string`.
Default: Absolute path to your home directory.
Directory where the search will stop.
### cache
Type: `boolean`.
Default: `true`.
If `false`, no caches will be used.
Read more about ["Caching"](#caching) below.
### transform
Type: `(Result) => Promise<Result> | Result`.
A function that transforms the parsed configuration. Receives the [result].
If using [`search()`] or [`load()`] \(which are async), the transform function can return the transformed result or return a Promise that resolves with the transformed result.
If using [`searchSync()`] or [`loadSync()`], the function must be synchronous and return the transformed result.
The reason you might use this option — instead of simply applying your transform function some other way — is that *the transformed result will be cached*. If your transformation involves additional filesystem I/O or other potentially slow processing, you can use this option to avoid repeating those steps every time a given configuration is searched or loaded.
### ignoreEmptySearchPlaces
Type: `boolean`.
Default: `true`.
By default, if [`search()`] encounters an empty file (containing nothing but whitespace) in one of the [`searchPlaces`], it will ignore the empty file and move on.
If you'd like to load empty configuration files, instead, set this option to `false`.
Why might you want to load empty configuration files?
If you want to throw an error, or if an empty configuration file means something to your program.
## Caching
As of v2, cosmiconfig uses caching to reduce the need for repetitious reading of the filesystem or expensive transforms. Every new cosmiconfig instance (created with `cosmiconfig()`) has its own caches.
To avoid or work around caching, you can do the following:
- Set the `cosmiconfig` option [`cache`] to `false`.
- Use the cache-clearing methods [`clearLoadCache()`], [`clearSearchCache()`], and [`clearCaches()`].
- Create separate instances of cosmiconfig (separate "explorers").
## Differences from [rc](https://github.com/dominictarr/rc)

@@ -243,4 +463,32 @@

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
And please do participate!
[result]: #result
[`load()`]: #explorerload
[`loadsync()`]: #explorerloadsync
[`search()`]: #explorersearch
[`searchsync()`]: #explorersearchsync
[`clearloadcache()`]: #explorerclearloadcache
[`clearsearchcache()`]: #explorerclearsearchcache
[`clearcaches()`]: #explorerclearcaches
[`packageprop`]: #packageprop
[`cache`]: #cache
[`stopdir`]: #stopdir
[`searchplaces`]: #searchplaces
[`loaders`]: #loaders
[`cosmiconfigoptions`]: #cosmiconfigoptions
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc