Proposal for a new v5 options scheme to allow for all kinds of flexibility
See original GitHub issueWe’ve had quite a few requests, some recent and some older, asking for more flexible ways to find, name, and parse files. Here’s what I dug up:
- ES2015 modules
- Whatever ES2015+ syntax Babel can handle
- Alternate JS extensions
- TypeScript
- JSON5
.config.js
files starting a dot, like.foo.config.js
- A subproperty in
package.json
nested under aconfig
property, like{"config":{"foo":{..}}}
- Searching within a
.config/
subdirectory of each searched directory
We’ve also had a discussion about the way extensionless rc files are parsed. We’ve talked about adding a parseJs
option (to address the request above for alternate flavors of JS); and, as was inevitable, the suggestion came up for parseJson
and parseYaml
options to allow for alternate flavors of those languages — which got me anticipating eventual requests to parse XML or INI or yet-unheard-of languages. It also got me looking at the current rc
, rcStrictJson
, and rcExtensions
options, and realizing how conceptually tangled they are.
So, I’m wondering if there’s a way we could set up v5 to allow for any filenames, searched for in any order, and parsed with any parsers.
Here’s what I’ve come up with:
What if we do away with most of the existing options (packageProp
, rc
, js
, rcStrictJson
, and rcExtensions
), and instead have a flexible searchScheme
option.
searchScheme
is an array representing an ordered list of things to search for in each directory.- Each item in the array is either an object or a string.
- Object items can have the following properties:
filename
: The filename of a file to search for, relative to the directory being searched; e.g..stylelintrc
,.stylelintrc.json
,stylelint.config.js
,package.json
. Could even include a directory if you wanted to search in a.config/
subdirectory of every searched directory.parser
: A parser function to use. Optional: If this is not provided, we’ll parse.js
as JS,.json
as JSON,.yaml
and.yml
as YAML, and extensionless rc files as YAML (which incorporates JSON). We’d expose some standard parsers on thecosmiconfig
object that people could use here, or they could bring their own.property
: A property to look for in the file. Optional: The main use case for this ispackage.json
, and by default it will match themoduleName
you provided when you initialized cosmiconfig. We could even accept an array to indicate nesting, to address the request above forconfig.mything
withinpackage.json
.
- String items are the equivalent of providing only a
filename
on an object and accepting the other defaults.
Here are a bunch of examples to illustrate how searchScheme
would work to address all these use cases. They all assume your tool’s name is plod
.
// The current default settings, verbose.
a = [
{
filename: 'package.json',
property: 'plod',
parser: cosmiconfig.parseJson
},
{
filename: '.plodrc',
parser: cosmiconfig.parseYaml
},
{
filename: 'plod.config.js',
parser: cosmiconfig.parseJs
}
]
// The current default settings, concise.
b = [
'package.json',
'.plodrc',
'plog.config.js'
]
// The current default plus rcStrictJson: true,
// so not allowing YAML in extensionless rc files.
c = [
'package.json',
{
filename: '.plodrc',
parser: cosmiconfig.parseJson
},
'plod.config.js'
]
// The current default plus rcExtensions: true,
// with string entries. **I suggest we make this
// the new default.**
d = [
'package.json',
'.plodrc',
'.plodrc.json',
'.plodrc.yaml',
'.plodrc.yml',
'.plodrc.js',
'plod.config.js'
]
// ESLint could do this.
// cf. https://eslint.org/docs/user-guide/configuring#configuration-file-formats
e = [
'.eslintrc.js',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
'.eslintrc',
{
filename: 'package.json',
property: 'eslintConfig'
}
]
// Babel could do this.
// cf. https://babeljs.io/docs/usage/babelrc/
f = [
'package.json',
{
filename: '.babelrc',
parser: someJson5Parser
}
]
// Allow a JS file with ES2015 module syntax
// and .js or .mjs extensions; or a .ts file;
g = [
{
filename: 'plod.js',
parser: someEs2015ModuleParser
},
{
filename: 'plod.mjs',
parser: someEs2015ModuleParser
},
{
filename: 'plod.ts',
parser: someTypeScriptParser
}
]
// Allow extensionless YAML and JSON files
// within or outside of .config/ subdirectories.
h = [
{
filename: '.plodrc',
parser: cosmiconfig.parseYaml
},
{
filename: '.config/.plodrc',
parser: cosmiconfig.parseYaml
}
]
// Look within the config property of package.json.
i = [
{
filename: 'package.json',
property: ['config', 'plod']
}
]
This proposal wouldn’t affect the way load
/loadSync
and search
/searchSync
or the caches work — just the way you configure cosmiconfig to search for and parse the formats you want. I think it could work, and also provide the possibility for indefinite extension without the need to add more options in the future. (At most, we might end up adding more defaults or exposing more parsers in the future, if we find that some unaddressed usage is super common.)
I suggest that we add rc files with extensions to the default configuration because I think it’s being used all over the place. I know that stylelint, Prettier, and lint-staged use it.
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (8 by maintainers)
Top GitHub Comments
@olsonpm: That is a very good and devastating point. This whole idea doesn’t address
load
very well, does it. 🤔Maybe we should consider the following variation:
loaders
,searchPlaces
, andpackageProp
.loaders
maps extensions to loaders. Loaders are functions that accept a file’s path and its contents and return a config object ornull
. Usually they’ll be sync because they already have the file’s contents, just have to parse it in some way. Aloaders
value can be a single loader function, which we’ll assume to be sync, or an object exposingsync
andasync
function.js
,json
,yaml
,yml
, andno-extension
all have default loaders; and those default loaders will be exposed oncosmiconfig.loadJson
, etc. Users could setjs
to their Babel-powered loader, ormjs
to their ES2015 module loader, orts
to their TypeScript loader, orini
to their INI loader. They could also setno-extension
tocosmiconfig.loadJson
if they want to only allow strict JSON.loaders
can apply to bothsearch
andload
.searchPlaces
is just an array of paths (no objects), determining which files we look at, in which order. It only applies tosearch
.packageProp
determines which property to look for inpackage.json
, same as now. We won’t build in the ability to get nested properties on other config files, but users could do that with custom loaders.I think this plan would address all the use cases described above and also handle
load
.Implemented in #129.