cosmiconfig
cosmiconfig copied to clipboard
Respect the XDG base directory specification
Hello,
I searched in the issues here and shockingly I haven't found this mentioned.
Currently, cosmiconfig searches up the parent tree from the working directory, stopping at $HOME
, which is fine and most, I would assume, are okay with this.
However, for filesystem psychopaths like myself, it pains me to have to move any sort of user configuration file out $XDG_CONFIG_HOME/project-name/
directory (usually, ~/.config/project-name/
) and pollute my home directory.
Any thoughts on adding this as a final search path?
You can read up on the specification here and here.
In short, using prettier as an example, I'd imagine the workflow to go like this...
- Perform the current lookup traversal as it is done right now.
- If nothing found, check the following:
-
$XDG_CONFIG_HOME/prettier/config
-
$XDG_CONFIG_HOME/prettier/[...current standard file name checks]
Thanks for considering.
this is one of the reasons davidtheclark added searchPlaces. if you find it doesn't suit your needs then let me know.
Thanks @dsifford. @olsonpm is right that this should be possible already by using searchPlaces
. So at this point your feature request is for the cosmiconfig consumer, maybe Prettier.
It makes sense to look in XDG_CONFIG_HOME
by default, doesn't it?
@SimenB We've still heard very minimal feedback that this is important to enough people and conventional enough that it should be included by default. We have 2 voices in this issue, 3 in https://github.com/prettier/prettier/issues/4810, and the discussion started more than 6 months ago. If Prettier has only received a few thumbs about it and has not addressed it themselves, despite being able to do so with searchPlaces
; and no other Cosmiconfig users have asked for it; then I'm not sure there's enough weight behind it for a change to the library.
Reopening the issue to encourage more people to chime in.
@davidtheclark After looking through closed issues, I think this really is a symptom of a more general problem.
End users don't have much control over where their config files are located
I'm making an assumption here, but I'm assuming that one of cosmiconfig's goals is to actually give end users more control over where their config files are located. Indeed, cosmiconfig does give end users more control "on paper" -- but in practice, not really.
Various tooling is setup to look in specific places for configs. If you specify a custom configuration directory, you have to specify this everywhere that tool is called from. Prettier is a good example of this.
This wouldn't normally be an issue on it's own. It's only an issue in this modern era of JS dev where we've had an explosion of "configurable tooling".
Our repos now have more config in the root dir than anything else on average and it's beginning to (for me at least) become a burden.
It would be great to have a solution like OP pointed out, or as others have suggested: <project_root>/.config
I know that adding searchPaths
has made such functionality possible, however, that relies on library authors to implement which practically guarantees there will be no standard.
cosmiconfig is growing rapidly and quickly becoming the de-facto config loading library. (congrats btw. 😃)
That means cosmiconfig has a golden opportunity to be a leader in standardizing / consolidating schemes.
A number of us have a preferred solution to the problem, but perhaps this could all be solved by simply letting the end user customize searchPaths
somehow. (maybe using package.json?)
Alternatively, comsmiconfig could just go the way of prettier and be opinionated and offer no configuration and set the standard that way.
The core problem for me is that my projects are cluttered with config files at the top level and I'd like to have the ability to personally wrangle that.
I'm making an assumption here, but I'm assuming that one of cosmiconfig's goals is to actually give end users more control over where their config files are located
below is my understanding of cosmiconfig. davidtheclark might think differently
One difference in our understandings is that cosmiconfig offers library authors more control over where their config files are located, which changes your premise a bit. Granted, like you say, we do set the defaults so end-users are ultimately affected.
Another difference is that cosmiconfig is a lower level tool that isn't meant to be opinionated. Since cosmiconfig aims to allow library authors the flexibility they need, we also don't want to set standards without the library authors' appeal - hence why this issue remains open.
Hopefully that helps.
just commenting to offer up https://github.com/sindresorhus/env-paths#pathsconfig which respects the xdg spec.
this is one of the reasons davidtheclark added searchPlaces . if you find it doesn't suit your needs then let me know.
searchPlaces
are searched for each one of the folders while moving up, checking on each one of the folders. If we add a new "place" pointing inside $HOME
, it will sucess on the first try, ignoring the other parent folders. I think we need a new fallbackSearchPlaces
to search when no config has been found when searching in parent folders, so users can add there $HOME
or /etc
or whatever other default common config places.
@olsonpm I'd consider myself a library author (or at least most of the stuff I work on isn't applications), but I really don't want to add duplicated config files to every single repository I ever work on. I'd much rather have my config in a centralized repository that I can check out to a well specified path on my system (and share with someone who wants to collaborate with me). This is why I believe obeying the XDG config directory specification would be a good idea.
This way if I need configuration changes they happen in a centralized place and don't need to be copy pasted into every single repository I work on.
Strong agree that searchPlaces
should default to XDG compliance. Library authors (by and large) will not change the defaults. So cosmiconfig's defaults will perpetuate to all users of all libs which use cosmicconfig. In one swift PR, cosmiconfig could give control to all the users of all the cosmic-dependent libs by just making searchPlaces default to XDG's directories.
Open to a PR implementing this idea 🚀
I am working on putting together a PR for this. I think I have the impl, but now thinking about the best way to test. Gotta keep up that 💯 coverage!
Just posted this note at the top of the README:
MAINTAINERS WANTED! If you're interested in taking over and maintaining Cosmiconfig, please let @davidtheclark know (with an issue or email). I'd like to hand over the keys completely, so I'm looking for owners, not people who just want to merge a PR or two! You can make the decisions about what happens in v8 and subsequent versions, how the package balances stability and opinionated features, and so on. Take a look at open issues and PRs to learn about possibilities that have been on people's minds over the years.
If you're interested in taking ownership of the package and moving this feature forward, let me know!
Just posted this note at the top of the README:
MAINTAINERS WANTED! If you're interested in taking over and maintaining Cosmiconfig, please let @davidtheclark know (with an issue or email). I'd like to hand over the keys completely, so I'm looking for owners, not people who just want to merge a PR or two! You can make the decisions about what happens in v8 and subsequent versions, how the package balances stability and opinionated features, and so on. Take a look at open issues and PRs to learn about possibilities that have been on people's minds over the years.
If you're interested in taking ownership of the package and moving this feature forward, let me know!
I've thrown my hat into the ring to help maintain this amazing module.
Just to understand the call for XDG-by-default:
I do know that end user software generally has configuration files saved somewhere in the user's immediate scope. However, in most cases, it doesn't make sense to use, for example, a global Prettier config file.
There could be problems caused by configuration files outside of the repository.
Suppose you're forking a library in order to send a pull request to it. There's a few different scenarios:
- The library doesn't use prettier, so nothing should ever reformat the files in the repository. Nothing changes from the status quo as long as nothing still tries to run prettier, which would be an odd case.
- The library already uses prettier with a custom config file, so according to the premise, that config file in its repository takes priority over the one in the home folder. Nothing changes from the status quo.
- The library uses prettier with defaults. This is the dangerous one. The config file is not found in the repo, so it's being found in the XDG config directory, overriding the defaults. Then your IDE automatically picks up that the repo uses prettier (from package.json, node_modules, whatever), runs a format using the settings from your global config and then the PR you're sending is riddled with unnecessary reformats that the original maintainer does not want.
This is only one use case, but I'd argue that in most cases, accepting a global config by default would work similarly against the original maintainer of repositories using cosmiconfig if they accept PRs from strangers.
Therefore, I'd say that XDG should not be enabled by default. I do still see the benefit of offering it as an option.
I have some other possibilities in mind that could give some control back to the end user (e.g. the user of prettier), but XDG-by-default breaks expectations too hard to be viable IMO.
I'd like some opinions on this, especially from @SimenB and @jasonkarns who advocated for enabling this by default in this issue.
@TillaTheHun0 I'd definitely take your PR if someone figures out a good way to test this. (Might just override the XDG_CONFIG_DIR env var before loading in xdg-basedir
, maybe?)
Currently, cosmiconfig searches up the parent tree from the working directory, stopping at $HOME, which is fine and most, I would assume, are okay with this.
Keep doing this but at a last step look for the config file at a XDG dir.
The library uses prettier with defaults. This is the dangerous one. The config file is not found in the repo, so it's being found in the XDG config directory, overriding the defaults. Then your IDE automatically picks up that the repo uses prettier (from package.sjon, node_modules, whatever), runs a format using the settings from your global config and then the PR you're sending is riddled with unnecessary reformats that the original maintainer does not want.
This is also an issue with a config file at $HOME
so looking for config at a XDG dir wouldn't make this worse.
This is also an issue with a config file at
$HOME
so looking for config at a XDG dir wouldn't make this worse.
Maybe a better default would then be to not traverse at all (i.e. stopDir = cwd
) or maybe stop whenever a package.json is found in the currently traversed directory?
Thinking about the future major version here, there could be something like a global: true
mode that would both enable XDG compliance and set stopDir = $HOME
.
Maybe a better default would then be to not traverse at all (i.e. stopDir = cwd) or maybe stop whenever a package.json is found in the currently traversed directory?
This approach would be challenging when working with npm/yarn/pnpm workspaces
The way config lookup works for the vast majority of native applications that support XDG is to look up a config file in the xdg directory ie $XDG_CONFIG_HOME/[app]/config.ext
and if not present fallback to home directory. This approach differs from how cosmiconfig currently works.
I can see two main ways cosmiconfig could add support for XDG
- Once reaching home dir cosmiconfig could first check XDG directory and only then check home dir to respect XDG spec.
- Distinguish between project local config lookup and a global config lookup with a search strategy option.
Maybe a better default would then be to not traverse at all (i.e. stopDir = cwd) or maybe stop whenever a package.json is found in the currently traversed directory?
This approach would be challenging when working with npm/yarn/pnpm workspaces
Why though? Stuff like a linter or other directly project related things should generally always run in one of two contexts anyway:
- in the whole repo context, i.e. cwd = repo root and config file in the repo root as well
- in the context of a single package, i.e. cwd = package root and config file in the package root as well
Both cases should be well supported by the proposed approach.
Distinguish between project local config lookup and a global config lookup with a search strategy option.
That's basically a slightly more flexible version of my global: true
flag proposal. I can see us adopting this. But: if you say you might have problems with workspaces when limiting the traversal, how do you imagine "project local config lookup" could work?
Why though? Stuff like a linter or other directly project related things should generally always run in one of two contexts anyway:
- in the whole repo context, i.e. cwd = repo root and config file in the repo root as well
- in the context of a single package, i.e. cwd = package root and config file in the package root as well
Totally agree! My point was agains the idea of "stopping when encountering a directory containing package.json"
My point was agains the idea of "stopping when encountering a directory containing package.json"
So why is that a problem then? I legitimately don't see an issue with it, if the baseline is "don't traverse at all", then why not extend it to traverse until a package.json exists? (Could also make it an option/separate search strategy, maybe.)
@d-fischer You are correct. I was thinking about a case where for example a configuration would not be found in a workspaces project root due to the cwd being a workspace and the search stopping in a workspace package.
project-root
├── package.json
├── packages
│ └── foo
│ └── package.json
└── some.config.js
So if we run the script from project-root/packages/foo
the file project-root/some.config.js
won't be discovered with a strategy if the search stops at a directory containing package.json
Does this make sense?
Yes, this does make sense, and to me it seems absolutely fine to do it that way. This is exactly what I outlined above. If (for example) a linter is run within packages/foo
, I'd expect the linter being a direct dev dependency of that package (not of the root) and therefore the configuration file should live there too.
As a compromise, I could imagine a special configuration option being introduced with the same version as this. where you could explicitly declare other files to read and merge. For example:
.prettier.base.yml
printWidth: 80
semi: true
packages/foo/.prettier.yml
$import:
- ../../.prettier.base.yml
tabWidth: 4
useTabs: true
printWidth: 120
Resulting configuration object
{
semi: true,
tabWidth: 4,
useTabs: true,
printWidth: 120
}
This would be available to end users of all cosmiconfig consumers, using all file formats (it would just evaluate the resulting JS object) and would make it possible to access workspace configuration in subpackages without much duplication.
It could also automatically bring shared configuration outside of JS files to all cosmiconfig consumers (i.e. people could just include configuration baselines from npm even from non-JS configuration files).
(I'll probably open a separate issue for this relatively shortly.)
Would this address your concern?
All of the above sounds totally reasonable. Thanks for expanding on it 👍
Now added to the v9 branch and will be documented and released soon.