Cosmiconfig searches for and loads configuration for your program.
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 check the current directory for the following:
package.json
property.json
, .yaml
, .yml
, .js
, .ts
, .mjs
, or .cjs
.config
subdirectory.config.js
, .config.ts
, .config.mjs
, or .config.cjs
fileFor example, if your module's name is "myapp", cosmiconfig will search up the directory tree for configuration in the following places:
myapp
property in package.json
.myapprc
file in JSON or YAML format.myapprc.json
, .myapprc.yaml
, .myapprc.yml
, .myapprc.js
, .myapprc.ts
, .myapprc.mjs
, or .myapprc.cjs
filemyapprc
, myapprc.json
, myapprc.yaml
, myapprc.yml
, myapprc.js
, myapprc.ts
, myapprc.mjs
, or myapprc.cjs
file inside a .config
subdirectorymyapp.config.js
, myapp.config.ts
, myapp.config.mjs
, or myapp.config.cjs
fileOptionally, you can tell it to search up the directory tree using search strategies, checking each of these places in each directory, until it finds some acceptable configuration (or hits the home directory).
npm install cosmiconfig
Tested in Node 14+.
If you are an end user (i.e. a user of a tool that uses cosmiconfig, like prettier
or stylelint
),
you can skip down to the end user section.
Create a Cosmiconfig explorer, then either search
for or directly load
a configuration file.
const { cosmiconfig, cosmiconfigSync } = require('cosmiconfig');
// ...
const explorer = cosmiconfig(moduleName);
// 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.isEmpty is true if there was nothing to parse in the config file.
})
.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 explorerSync = cosmiconfigSync(moduleName);
const searchedFor = explorerSync.search();
const loaded = explorerSync.load(pathToConfig);
The result object you get from search
or load
has the following properties:
undefined
if the file is empty.true
if the configuration file is empty. This property will not be present if the configuration file is not empty.const { cosmiconfig } = require('cosmiconfig');
const explorer = cosmiconfig(moduleName, /* optional */ cosmiconfigOptions)
Creates a cosmiconfig instance ("explorer") configured according to the arguments, and initializes its caches.
Type: string
. Required.
Your module name. This is used to create the default searchPlaces
and packageProp
.
If your searchPlaces
value will include files, as it does by default (e.g. ${moduleName}rc
), your moduleName
must consist of characters allowed in filenames. That means you should not copy scoped package names, such as @my-org/my-package
, directly into moduleName
.
cosmiconfigOptions
are documented below.
You may not need them, and should first read about the functions you'll use.
explorer.search([searchFrom]).then(result => { /* ... */ })
Searches for a configuration file. Returns a Promise that resolves with a result or with null
, if no configuration file is found.
You can do the same thing synchronously with explorerSync.search()
.
Let's say your module name is goldengrahams
so you initialized with const explorer = cosmiconfig('goldengrahams');
.
Here's how your default search()
will work:
process.cwd()
(or some other directory defined by the searchFrom
argument to search()
), look for configuration objects in the following places:
goldengrahams
property in a package.json
file..goldengrahamsrc
file with JSON or YAML syntax..goldengrahamsrc.json
, .goldengrahamsrc.yaml
, .goldengrahamsrc.yml
, .goldengrahamsrc.js
, .goldengrahamsrc.ts
, .goldengrahamsrc.mjs
, or .goldengrahamsrc.cjs
file. (To learn more about how JS files are loaded, see "Loading JS modules".)goldengrahamsrc
, goldengrahamsrc.json
, goldengrahamsrc.yaml
, goldengrahamsrc.yml
, goldengrahamsrc.js
, goldengrahamsrc.ts
, goldengrahamsrc.mjs
, or goldengrahamsrc.cjs
file in the .config
subdirectory.goldengrahams.config.js
, goldengrahams.config.ts
, goldengrahams.config.mjs
, or goldengrahams.config.cjs
file. (To learn more about how JS files are loaded, see "Loading JS modules".)none
(which is the default if you don't specify a stopDir
option), stop here and return/resolve with null
.global
(which is the default if you specify a stopDir
option), move up one directory level and try again,
recursing until arriving at the configured stopDir
option, which defaults to the user's home directory.
stopDir
, the global configuration directory (as defined by env-paths
without prefix) is also checked,
looking at the files config
, config.json
, config.yaml
, config.yml
, config.js
, config.ts
, config.cjs
, and config.mjs
in the directory ~/.config/goldengrahams/
(on Linux; see env-paths
documentation for other OSs).project
, check whether a package.json
file is present in the current directory, and if not,
move up one directory level and try again, recursing until there is one.search()
Promise resolves with its result (or, with explorerSync.search()
, the result is returned).search()
Promise resolves with null
(or, with explorerSync.search()
, null
is returned).search()
Promise rejects with that error (so you should .catch()
it). (Or, with explorerSync.search()
, the error is thrown.)If you know exactly where your configuration file should be, you can use load()
, instead.
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.
Type: string
.
Default: process.cwd()
.
A filename.
search()
will start its search here.
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.
explorer.load(loadPath).then(result => { /* ... */ })
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).
Use load
if you already know where the configuration file is and you just need to load it.
explorer.load('load/this/file.json'); // Tries to load load/this/file.json.
If you load a package.json
file, the result will be derived from whatever property is specified as your packageProp
.
package.yaml
will work as well if you specify these file names in your searchPlaces
.
You can do the same thing synchronously with explorerSync.load()
.
Clears the cache used in load()
.
Clears the cache used in search()
.
Performs both clearLoadCache()
and clearSearchCache()
.
const { cosmiconfigSync } = require('cosmiconfig');
const explorerSync = cosmiconfigSync(moduleName, /* optional */ cosmiconfigOptions)
Creates a synchronous cosmiconfig instance ("explorerSync") configured according to the arguments, and initializes its caches.
See cosmiconfig()
.
const result = explorerSync.search([searchFrom]);
Synchronous version of explorer.search()
.
Returns a result or null
.
const result = explorerSync.load(loadPath);
Synchronous version of explorer.load()
.
Returns a result.
Clears the cache used in load()
.
Clears the cache used in search()
.
Performs both clearLoadCache()
and clearSearchCache()
.
Type: Object
.
Possible options are documented below.
Type: string
Default: global
if stopDir
is specified, none
otherwise.
The strategy that should be used to determine which directories to check for configuration files.
none
: Only checks in the current working directory.project
: Starts in the current working directory, traversing upwards until a package.{json,yaml}
file is found.global
: Starts in the current working directory, traversing upwards until the configured stopDir
(or the current user's home directory if none is given). Then, if no configuration is found, also look in the
operating system's default configuration directory (according to env-paths
without prefix),
where a different set of file names is checked:[
`config`,
`config.json`,
`config.yaml`,
`config.yml`,
`config.js`,
`config.ts`,
`config.cjs`,
`config.mjs`
]
Type: Array<string>
.
Default: See below.
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.
Default searchPlaces
:
For the asynchronous API, these are the default searchPlaces
:
[
'package.json',
`.${moduleName}rc`,
`.${moduleName}rc.json`,
`.${moduleName}rc.yaml`,
`.${moduleName}rc.yml`,
`.${moduleName}rc.js`,
`.${moduleName}rc.ts`,
`.${moduleName}rc.mjs`,
`.${moduleName}rc.cjs`,
`.config/${moduleName}rc`,
`.config/${moduleName}rc.json`,
`.config/${moduleName}rc.yaml`,
`.config/${moduleName}rc.yml`,
`.config/${moduleName}rc.js`,
`.config/${moduleName}rc.ts`,
`.config/${moduleName}rc.mjs`,
`.config/${moduleName}rc.cjs`,
`${moduleName}.config.js`,
`${moduleName}.config.ts`,
`${moduleName}.config.mjs`,
`${moduleName}.config.cjs`,
];
For the synchronous API, the only difference is that .mjs
files are not included. See "Loading JS modules" for more information.
Create your own array to search more, fewer, or altogether different places.
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.
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.
package.yaml
(used by pnpm) works the same way.
Examples, with a module named porgy
:
// Disallow extensions on rc files:
['package.json', '.porgyrc', 'porgy.config.js']
// Limit the options dramatically:
['package.json', '.porgyrc']
// 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 a custom loader to tell
// Cosmiconfig how to handle `.coffee` files.
// Look within a .config/ subdirectory of every searched directory:
[
'package.json',
'.porgyrc',
'.config/.porgyrc',
'.porgyrc.json',
'.config/.porgyrc.json'
]
Type: Object
.
Default: See below.
An object that maps extensions to the loader functions responsible for loading and parsing files with those extensions.
Cosmiconfig exposes its default loaders on the named export defaultLoaders
and defaultLoadersSync
.
Default loaders
:
const { defaultLoaders, defaultLoadersSync } = require('cosmiconfig');
console.log(Object.entries(defaultLoaders));
// [
// [ '.mjs', [Function: loadJs] ],
// [ '.cjs', [Function: loadJs] ],
// [ '.js', [Function: loadJs] ],
// [ '.ts', [Function: loadTs] ],
// [ '.json', [Function: loadJson] ],
// [ '.yaml', [Function: loadYaml] ],
// [ '.yml', [Function: loadYaml] ],
// [ 'noExt', [Function: loadYaml] ]
// ]
console.log(Object.entries(defaultLoadersSync));
// [
// [ '.cjs', [Function: loadJsSync] ],
// [ '.js', [Function: loadJsSync] ],
// [ '.ts', [Function: loadTsSync] ],
// [ '.json', [Function: loadJson] ],
// [ '.yaml', [Function: loadYaml] ],
// [ '.yml', [Function: loadYaml] ],
// [ 'noExt', [Function: loadYaml] ]
// ]
(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.)
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 .myapprc
.
Values in loaders
are a loader function (described below) 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:
{
noExt: defaultLoaders['.json'];
}
If you want to load files that are not handled by the loader functions Cosmiconfig exposes, you can write a custom loader function or use one from NPM if it exists.
Use cases for custom loader function:
Custom loader functions have the following signature:
// Sync
type SyncLoader = (filepath: string, content: string) => Object | null
// Async
type AsyncLoader = (filepath: string, content: string) => Object | null | Promise<Object | null>
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.
A few things to note:
cosmiconfigSync()
).require
hook, because defaultLoaders['.js']
just uses require
.
Whether you use custom loaders or a require
hook is up to you.Examples:
// Allow JSON5 syntax:
cosmiconfig('foo', {
loaders: {
'.json': json5Loader
}
});
// Allow a special configuration syntax of your own creation:
cosmiconfig('foo', {
loaders: {
'.special': specialLoader
}
});
// Allow many flavors of JS, using custom loaders:
cosmiconfig('foo', {
loaders: {
'.coffee': coffeeScriptLoader
}
});
// Allow many flavors of JS but rely on require hooks:
cosmiconfig('foo', {
loaders: {
'.coffee': defaultLoaders['.js']
}
});
Type: string | Array<string>
.
Default: `${moduleName}`
.
Name of the property in package.json
(or package.yaml
) to look for.
Use a period-delimited string or an array of strings to describe a path to nested properties.
For example, the value 'configs.myPackage'
or ['configs', 'myPackage']
will get you the "myPackage"
value in a package.json
like this:
{
"configs": {
"myPackage": {"option": "value"}
}
}
If nested property names within the path include periods, you need to use an array of strings. For example, the value ['configs', 'foo.bar', 'baz']
will get you the "baz"
value in a package.json
like this:
{
"configs": {
"foo.bar": {
"baz": {"option": "value"}
}
}
}
If a string includes period but corresponds to a top-level property name, it will not be interpreted as a period-delimited path. For example, the value 'one.two'
will get you the "three"
value in a package.json
like this:
{
"one.two": "three",
"one": {
"two": "four"
}
}
Type: string
.
Default: Absolute path to your home directory.
Directory where the search will stop.
Type: boolean
.
Default: true
.
If false
, no caches will be used.
Read more about "Caching" below.
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 cosmiconfigSync
, search()
or load()
, 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.
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.
Your end users can provide JS configuration files as ECMAScript modules (ESM) under the following conditions:
--experimental-modules
flag)..mjs
configuration file, or a .js
file whose nearest parent package.json
file contains "type": "module"
. (See Node's method for determining a file's module system.)With cosmiconfig's asynchronous API, the default searchPlaces
include .js
, .ts
, .mjs
, and .cjs
files. Cosmiconfig loads all these file types with the dynamic import
function.
With the synchronous API, JS configuration files are always treated as CommonJS, and .mjs
files are ignored, because there is no synchronous API for the dynamic import
function.
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:
cosmiconfig
option cache
to false
.clearLoadCache()
, clearSearchCache()
, and clearCaches()
.rc serves its focused purpose well. cosmiconfig differs in a few key ways — making it more useful for some projects, less useful for others:
package.json
property, an rc file, a .config.js
file, and rc files with extensions.When configuring a tool, you can use multiple file formats and put these in multiple places.
Usually, a tool would mention this in its own README file,
but by default, these are the following places, where {NAME}
represents the name of the tool:
package.json
.{NAME}rc
.{NAME}rc.json
.{NAME}rc.yaml
.{NAME}rc.yml
.{NAME}rc.js
.{NAME}rc.ts
.{NAME}rc.cjs
.config/{NAME}rc
.config/{NAME}rc.json
.config/{NAME}rc.yaml
.config/{NAME}rc.yml
.config/{NAME}rc.js
.config/{NAME}rc.ts
.config/{NAME}rc.mjs
.config/{NAME}rc.cjs
{NAME}.config.js
{NAME}.config.ts
{NAME}.config.mjs
{NAME}.config.cjs
The contents of these files are defined by the tool.
For example, you can configure prettier to enforce semicolons at the end of the line
using a file named .config/prettierrc.yml
:
semi: true
Additionally, you have the option to put a property named after the tool in your package.json
file,
with the contents of that property being the same as the file contents. To use the same example as above:
{
"name": "your-project",
"dependencies": {},
"prettier": {
"semi": true
}
}
This has the advantage that you can put the configuration of all tools (at least the ones that use cosmiconfig) in one file.
You can also add a cosmiconfig
key within your package.json
file or create one of the following files
to configure cosmiconfig
itself:
.config/config.json
.config/config.yaml
.config/config.yml
.config/config.js
.config/config.ts
.config/config.cjs
The following properties are currently actively supported in these places:
cosmiconfig:
# adds places where configuration files are being searched
searchPlaces:
- .config/{name}.yml
# to enforce a custom naming convention and format, don't merge the above with the tool-defined search places
# (`true` is the default setting)
mergeSearchPlaces: false
Note: technically, you can overwrite all options described in cosmiconfigOptions here, but everything not listed above should be used at your own risk, as it has not been tested explicitly. The only exceptions to this are the
loaders
property, which is explicitly not supported at this time, and thesearchStrategy
property, which is intentionally disallowed.
You can also add more root properties outside the cosmiconfig
property
to configure your tools, entirely eliminating the need to look for additional configuration files:
cosmiconfig:
searchPlaces: []
prettier:
semi: true
Wherever you put your configuration (the package.json file, a root config file or a package-specific config file),
you can use the special $import
key to import another file as a base.
For example, you can import from an npm package (in this example, @foocorp/config
).
.prettierrc.base.yml
in said npm package could define some company-wide defaults:
printWidth: 120
semi: true
tabWidth: 2
And then, the .prettierrc.yml
file in the project itself would just reference that file,
optionally overriding the defaults with project-specific settings:
$import: node_modules/@foocorp/config/.prettierrc.base.yml
# we want more space!
printWidth: 200
It is possible to import multiple base files by specifying an array of paths, which will be processed in declaration order; that means that the last entry will win if there are conflicting properties.
It is also possible to import file formats other than the importing format as long as they are supported by the loaders specified by the developer of the tool you're configuring.
$import: [first.yml, second.json, third.config.js]
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.
And please do participate!