seniority
Recipes

Incremental migration from cosmiconfig

Swap cosmiconfig for seniority in one import, then move to discover() and resolve() when you want precedence and provenance — and what changes at each step.

1. Change the import

- import { cosmiconfig } from 'cosmiconfig';
+ import { cosmiconfig } from 'seniority';

The explorer is cosmiconfig's (search, load, clearCaches), graded 242 of 243 by cosmiconfig's own suite, the same as cosmiconfig itself. YAML config files are read as cosmiconfig reads them, with no parser to add: seniority/yaml is loaded on the first one. npx burgee migrate makes this swap for you.

For lilconfig the swap is seniority/lilconfig, graded 77 of 77, and npx burgee migrate makes it for you.

2. Resolve instead of merging by hand

A cosmiconfig caller usually merges the file with flags and the environment itself, in an order written once per program. discover plus resolve replaces that code, and keeps the record:

migrated.mjs
import { writeFileSync } from 'node:fs';

import { cosmiconfig, discover, explain, resolve } from 'seniority';

writeFileSync('mytool.config.json', '{ "region": "eu-2", "retries": 3 }\n');
const env = { MYTOOL_RETRIES: '5' };
const flags = { region: 'ap-1' };

// Before: find the file, then merge by hand.
const found = await cosmiconfig('mytool', { searchPlaces: ['mytool.config.json'] }).search();
const before = { ...found.config, ...(env.MYTOOL_RETRIES && { retries: Number(env.MYTOOL_RETRIES) }), ...flags };
console.log('before:', before);

// After: one order, and the answer to "why".
const specs = { region: { type: 'string' }, retries: { type: 'number' } };
const result = resolve(specs, { flags, env, envPrefix: 'MYTOOL', config: await discover({ name: 'mytool', cwd: '.', env }) });
console.log('after: ', result.values);
process.stdout.write(explain('retries', result));
node migrated.mjs
before: { region: 'ap-1', retries: 5 }
after:  { region: 'ap-1', retries: '5' }
retries = "5"   from env MYTOOL_RETRIES
         candidates: flag --retries (unset), config file mytool.config.json:1 3, default (unset)

Note the one difference in the output: the environment value is the string "5". resolve converts only booleans from the environment. Declare the shape and run validate (see Validation) to catch a number that arrives as a string, or convert it where you read it.

What changes when you move:

  • The order is fixed: flag, env, config, package.json, default.
  • The environment reaches declared options only, under the prefix.
  • discover looks in fewer places than cosmiconfig's default search places: an explicit file, <NAME>_CONFIG, <name>.config.* in the working directory, and the user config directory. It walks upward only when asked.

On this page