Composable Options

Extract common options into reusable functions and compose them across multiple commands. Shows two approaches: manual generics and the makeComposableBuilder helper.

Also demonstrates accessing child commands programmatically via getChildren() and invoking their handlers with full type inference.

All Example Files

FILE EXPLORER
composable-options.ts
1import { UnknownCLI, chain, cli, makeComposableBuilder } from 'cli-forge';
2
3// -- Reusable option definitions --
4
5// Manual generic approach (uses UnknownCLI as the constraint)
6function withName<T extends UnknownCLI>(argv: T) {
7  return argv.option('name', {
8    type: 'string',
9    description: 'Your name',
10    required: true,
11  });
12}
13
14const withGreeting = makeComposableBuilder((args) =>
15  args.option('greeting', {
16    type: 'string',
17    description: 'The greeting to use',
18    default: 'Hello',
19  })
20);
21
22const withFarewell = makeComposableBuilder((args) =>
23  args.option('farewell', {
24    type: 'string',
25    description: 'The farewell to use',
26    default: 'Goodbye',
27  })
28);
29
30// -- Commands with return values --
31
32const withGreetCommand = makeComposableBuilder((args) =>
33  args.command('greet', {
34    builder: (args) => chain(args, withName, withGreeting),
35    handler: (args): string => {
36      const message = `${args.greeting}, ${args.name}!`;
37      console.log(message);
38      return message;
39    },
40  })
41);
42
43const withFarewellCommand = makeComposableBuilder((args) =>
44  args.command('farewell', {
45    builder: (args) => chain(args, withName, withFarewell),
46    handler: (args): string => {
47      const message = `${args.farewell}, ${args.name}!`;
48      console.log(message);
49      return message;
50    },
51  })
52);
53
54const withConverseCommand = makeComposableBuilder((args) =>
55  args.command('converse', {
56    description: 'A quick chat',
57    builder: (args) => chain(args, withName),
58    handler: (args) => {
59      console.log(`[${args.name}]: hello!`);
60    },
61  })
62);
63
64// -- Parent command accessing children --
65
66cli('composable-options', {
67  builder: (args) =>
68    chain(args, withGreetCommand, withFarewellCommand, withConverseCommand),
69
70  handler: async (_args, ctx) => {
71    const children = ctx.command.getChildren();
72
73    // Handlers are typed - greetHandler returns string
74    const greetHandler = children.greet.getHandler();
75    if (greetHandler) {
76      const result = greetHandler({ name: 'Alice', greeting: 'Hi' });
77      console.log('Greeting ends with !:', result.endsWith('!'));
78    }
79
80    console.log('Available commands:', Object.keys(children).join(', '));
81  },
82}).forge();
83