seniority
Coming from…

Coming from rc

An rc alternative with a drop-in path: import rc from seniority/rc, graded 1 / 1 by rc's own test — one exit-code bit, not a case count — then rc's file stack and merge with none of its four dependencies, and a resolver that says where every value came from.

seniority is an rc alternative that also covers what cosmiconfig, dotenv and find-up do beside it: one resolution for flags, environment, config files, a package.json field and defaults, in a fixed order, with no dependencies.

Migrate from rc in one import

- import rc from 'rc';
+ import rc from 'seniority/rc';

The call is rc's — rc(name, defaults, argv, parse) — and so is what it does: the file stack in rc's own order (/etc/<name>/config, /etc/<name>rc, the four places under the home directory, the nearest .<name>rc walking up from the working directory, then a file named by config in the environment or on the command line), <name>_* environment keys with __ read as nesting, JSON with comments, deep-extend's merge (arrays replaced, not concatenated), a string defaults read as JSON, and the configs / config report of which files were read. Like rc, it reads the process's environment when you pass none.

Two differences, both on purpose.

  • INI is refused by name, not parsed. seniority bundles no format parser, so a file that is not JSON is refused with the fix in the error. Pass ini.parse in rc's own fourth position and you have rc's behaviour, with the parser as your dependency rather than everyone's.
  • The command line is an argument. When argv is falsy, rc 1.2.8 fills it with minimist(process.argv.slice(2)); seniority/rc treats an omitted argv as empty and never reads the command line. Pass the flags you already parsed as the third argument.
import rc from 'seniority/rc';

const config = rc('mytool', defaults, flags);

A fifth argument, { env, cwd, home, win }, supplies the rest of the world when you would rather not have it read from the process — which is how it is tested.

Is seniority compatible with rc?

Graded, and the grade is coarse. rc's test/test.js, vendored from 1.2.8 and unmodified apart from the import specifier, runs against seniority/rc beside a control that runs it against real rc:

passinggrade
seniority/rc1 / 1by exit code
rc itself (control)1 / 1by exit code

From Compatibility, which npm run compat:page generates from the oracle's last run; that page is the authority. Read 1 / 1 as one bit, not one case: rc's suite is scripts of bare assert calls with no reporter, so the only honest grade is that the script ran against this target and exited 0. That script covers an environment option, a nopt-shaped argv outranking it, and a commented JSON .<name>rc in the working directory with its config and configs report. rc 1.2.8 was never tagged upstream, so the suite is taken from the repository's head at commit a97f6ad. rc's two other test files are not graded: ini.js tests an internal and the ini package, and nested-env-vars.js is public and not graded yet.

The same page grades seniority (cosmiconfig's own suite), seniority/lilconfig and seniority/dotenv.

What you gain over rc

rc 1.2.8 depends on deep-extend, ini, minimist and strip-json-comments. seniority/rc depends on nothing. And where rc merges, seniority also resolves the value and keeps the receipt:

  • Provenance on every value. resolve() returns each option's winning source and every candidate it beat, where rc's configs only lists the files it read. explanationJson() is that record as --json data and explanationEvent() as an agent event, generated by the code that picked the value.
  • An order nobody can rearrange. flag > env > config file > package.json field > default, exported as ORDER. A plugin can add a source between them and can never beat the flag the user typed.
  • A bounded walk. The upward search for .<name>rc stops at sixty-four levels, so no directory tree can turn it into a hang.

When to switch from rc

  • Your rc files are JSON, or you are content to pass ini.parse in.
  • You already parse your own flags, and would rather rc did not parse process.argv a second time behind them.
  • "Why is this set to that?" is a question your users or agents ask.

The API, the discovery rules and the other drop-in paths are on seniority.

On this page