New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Help] Struggling to implement configuration file options #1843
Comments
A couple of historical references. There was a discussion about improvements to support config file processing in #1584. Several of the people commenting had already written implementations. The outcome was adding The use case in this comment is processing a config file: #1713 (comment) |
There are not methods to do this directly. I think the safest route is probably looking up the option and doing the conversion yourself. You could try emitting an option event (e.g. |
A small heads-up. The built-in processing has |
I was able to come up with this (in typescript using @commander-js/extra-typings): //cli.ts
import fs from "fs";
import path from "path";
import { createCommand, createOption } from "@commander-js/extra-typings";
const parser = function (value: string): string {
return value + "-parsed";
};
const optionA = createOption("--a <a>")
.makeOptionMandatory()
.env("A")
.default("default-A-parsed")
.argParser(parser);
const optionB = createOption("--b <b>")
.makeOptionMandatory()
.env("B")
.argParser(parser);
const useA = createCommand("useA")
.addOption(optionA)
.action((opts) => console.log(opts.a));
const useAandB = createCommand("useAandB")
.addOption(optionA)
.addOption(optionB)
.action((opts) => console.log(opts.a, opts.b));
await createCommand()
.addOption(createOption("--config <path>"))
.helpOption(false) // forward --help to inner program
.allowUnknownOption() // ignore options intended for inner program
.action(async (opts) => {
const config = opts.config ? await readConfig(opts.config) : {};
createCommand()
.addOption(createOption("--config <path>", "Path to configuration file")) // for help display
.hook("preSubcommand", (_, subCommand) => {
for (const [key, value] of Object.entries(config)) {
subCommand.setOptionValue(key, value);
}
})
.addCommand(useA)
.addCommand(useAandB)
.showHelpAfterError()
.parse();
})
.parseAsync();
async function readConfig(relativePath: string): Promise<any> {
const modulePath = path.resolve(relativePath);
if (fs.existsSync(modulePath) && fs.statSync(modulePath).isFile()) {
const module = await import(modulePath);
return module.default;
} else {
throw new Error(`Config file '${relativePath}' does not exist.`);
}
} Here I nest two root commands and use the Some observations:
|
Yes. A high level reply. I am open to adding more hooks as identify use cases that require them and how they can be identified, such as |
This is being considered and done for some new code where it makes sense, and for example the recent I don't think it is feasible to change the old methods, it would change and break too much existing code. |
I am interested to see you came up with a double-parse approach. I had wondered about that as a way to extract the |
👍 You had it worked out before I suggested it, good digging! |
Thanks for the detailed and thoughtful original question. I'll stop commenting, but ask if I haven't covered anything you haven't already worked out for yourself. 😄 |
The sample program has a lot in it, with defaults and env and mandatory and parser. Nice! For interest, I think there is no need to do the double parsing for this code. The await createCommand()
.addOption(createOption("--config <path>", "Path to configuration file"))
.hook("preSubcommand", async (hookedCommand, subCommand) => {
const configPath = hookedCommand.opts().config;
const config = configPath ? await readConfig(configPath) : {};
for (const [key, value] of Object.entries(config)) {
subCommand.setOptionValue(key, value);
}
})
.addCommand(useA)
.addCommand(useAandB)
.showHelpAfterError()
.parseAsync(); |
Opened a PR with some life cycle and hooks detail: #1860 |
Lots of good comments in this issue. The action item was to add some documentation on the parsing life cycle and hooks as a step forward. Shipped that in 10.0.1 Thanks @gomain |
Toy program:
Now I want to include a
config.json
file. Say it isSuch that when run
Use cases will extend to combinations of options having
.default
or.env
where precedence will be cli > config > env > default.I have looked into
.getOptionValue
and friends but struggle to find the right place to use them. Documentations lack practical examples.Some concerns:
.argParser
. I wish the json to store values as strings and be parsed as if provided on cli.--config <path>
to pass location of config file.The text was updated successfully, but these errors were encountered: