Skip to content

Commit dc27c9d

Browse files
committed
add example for option sharing
1 parent 6e53819 commit dc27c9d

16 files changed

+124
-62
lines changed

.changeset/fluffy-trains-complain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@eegli/tinyparse": patch
3+
---
4+
5+
Update docs to with shared/global options example

.changeset/great-ducks-brake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@eegli/tinyparse": minor
3+
---
4+
5+
Rename `setGlobals` to `globals` and `setMeta` to `meta`

docs/advanced-example.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ const parserOptions = new Parser()
4141
description: 'Comma-separated list of file extensions to include',
4242
});
4343

44-
const setGlobals = (opts: InferOptions<typeof parserOptions>) => {
44+
const globals = (opts: InferOptions<typeof parserOptions>) => {
4545
return {
4646
userName: 'me',
4747
extensions: opts.extensions.split(','),
4848
};
4949
};
5050

51-
const baseParser = parserOptions.setGlobals(setGlobals);
51+
const baseParser = parserOptions.globals(globals);
5252
type BaseParser = typeof baseParser;
5353

5454
// Define all subcommands
@@ -102,7 +102,7 @@ const handleDefault = ({ args, globals, options }: HandleDefaultArgs) => {
102102

103103
// Bring it all together
104104
const parser = baseParser
105-
.setMeta({
105+
.meta({
106106
command: 'my-cli',
107107
summary: 'Work with files and folders',
108108
help: {

docs/quickstart.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const parser = new Parser()
4343
shortFlag: '-v',
4444
defaultValue: false,
4545
})
46-
.setGlobals((options) => ({
46+
.globals((options) => ({
4747
getUserFromDB: (name: string) => `${name} Smith`,
4848
log: (message: string) => {
4949
if (options.verbose) {
@@ -62,7 +62,7 @@ const parser = new Parser()
6262
shortFlag: '-v',
6363
defaultValue: false,
6464
})
65-
.setGlobals((options) => ({
65+
.globals((options) => ({
6666
getUserFromDB: (name: string) => `${name} Smith`,
6767
log: (message: string) => {
6868
if (options.verbose) {
@@ -89,7 +89,7 @@ const parser = new Parser()
8989
shortFlag: '-v',
9090
defaultValue: false,
9191
})
92-
.setGlobals((options) => ({
92+
.globals((options) => ({
9393
getUserFromDB: (name: string) => `${name} Smith`,
9494
log: (message: string) => {
9595
if (options.verbose) {
@@ -105,7 +105,7 @@ const parser = new Parser()
105105
globals.log(`Hello, ${userName}!`);
106106
},
107107
})
108-
.setMeta({
108+
.meta({
109109
command: 'my-cli',
110110
summary: 'A brief description of my-cli',
111111
help: {
@@ -129,7 +129,7 @@ const parser = new Parser()
129129
shortFlag: '-v',
130130
defaultValue: false,
131131
})
132-
.setGlobals((options) => ({
132+
.globals((options) => ({
133133
getUserFromDB: (name: string) => `${name} Smith`,
134134
log: (message: string) => {
135135
if (options.verbose) {
@@ -145,7 +145,7 @@ const parser = new Parser()
145145
globals.log(`Hello, ${userName}!`);
146146
},
147147
})
148-
.setMeta({
148+
.meta({
149149
command: 'my-cli',
150150
summary: 'A brief description of my-cli',
151151
help: {

docs/reference/async-operations.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const parser = new Parser()
2323
required: true,
2424
defaultValue: '',
2525
})
26-
.setGlobals(async ({ token }) => {
26+
.globals(async ({ token }) => {
2727
// Use the token to establish a connection
2828
return { database: async (name: string) => name };
2929
})

docs/reference/globals.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
Often times, subcommands need access to a shared object such as a database connection or a logger. You can attach such an object in the building phase and it will be passed to the handler of each subcommand (and the default handler). Setting the globals is done via a function that has access to all the options that have been collected while parsing.
66

7-
Globals are expected to be static and should only be set once. _Calling `.setGlobals()` multiple times will override the previous value._
7+
Globals are expected to be static and should only be set once. _Calling `.globals()` multiple times will override the previous value._
88

99
```ts
1010
import { Parser } from '@eegli/tinyparse';
@@ -14,7 +14,7 @@ const globals = {
1414
};
1515

1616
new Parser()
17-
.setGlobals(() => globals)
17+
.globals(() => globals)
1818
.defaultHandler(({ globals }) => {
1919
const user = globals.database('John');
2020
console.log(`Hello, ${user}!`);
@@ -48,5 +48,5 @@ const globalSetter = (options: Options) => ({
4848
}
4949
},
5050
});
51-
const parser = parserOptions.setGlobals(globalSetter).defaultHandler();
51+
const parser = parserOptions.globals(globalSetter).defaultHandler();
5252
```

docs/reference/handlers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const baseParser = new Parser()
5252
longFlag: '--foo',
5353
defaultValue: 'default',
5454
})
55-
.setGlobals(() => ({
55+
.globals(() => ({
5656
bar: 'baz',
5757
}));
5858

docs/reference/help-and-meta.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
55
All good CLI apps have some way of providing help to the user. Tinyparse is no different. You can register usage information and tokens that, when present in the input, will trigger the help text to be printed to the console .
66

7-
You can register which a subcommand and flags trigger the help text and other metadata with the `.setMeta()` method. You need to do this _manually_. If you do not specify a help command or help flags, Tinyparse will not assume them for you.
7+
You can register which a subcommand and flags trigger the help text and other metadata with the `.meta()` method. You need to do this _manually_. If you do not specify a help command or help flags, Tinyparse will not assume them for you.
88

99
```ts
1010
import { Parser } from '@eegli/tinyparse';
1111

1212
const parser = new Parser()
13-
.setMeta({
13+
.meta({
1414
command: 'my-cli',
1515
summary: 'A brief description of my-cli',
1616
help: {
@@ -71,4 +71,4 @@ Optional flags
7171
-V, --version Print the version
7272
```
7373

74-
When you set your help and metadata configuration, Tinyparse will validate the arguments to make sure there are no conflicts with existing flags or subcommands. Note that subsequent calls to `.setMeta()` will overwrite the previous configuration.
74+
When you set your help and metadata configuration, Tinyparse will validate the arguments to make sure there are no conflicts with existing flags or subcommands. Note that subsequent calls to `.meta()` will overwrite the previous configuration.

docs/reference/subcommands.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const baseParser = new Parser()
8080
shortFlag: '-u',
8181
defaultValue: false,
8282
})
83-
.setGlobals(() => ({
83+
.globals(() => ({
8484
flower: '🌸',
8585
}));
8686

docs/reference/subparsers.md

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,59 @@ Subparsers can be seen as an _extension_ to subcommands. Although they behave an
1515

1616
Hence, if your app is rather complex and you want to have per-subcommand options (such as a dedicated help and options local to the subcommand), you should use a **subparser** rather than a subcommand. Note that both subcommands and subparsers are defined via a keyword and can live in the same parent parser. Subparsers can be nested arbitrarily deep.
1717

18-
## Adding a Subparser
18+
## Adding a Subparser with Shared Options
1919

20-
In the following example, we have a main and a subparser. The only difference between them is the version:
20+
By default - as mentioned above - a subparser acts in complete isolation and does not inherit any options from the parent. _Shared_ or _global_ options can still be achieved but they require a few manual type annotations. In the following example, we have a main and a subparser. They share the `--verbose` option.
21+
22+
1. First, let's define a function to create the shared options. Because parsers are generic, we need to explicitly annotate that the returned parser has the `verbose` option:
2123

2224
```ts
23-
const subparser = new Parser()
24-
.option('greeting', {
25-
longFlag: '--greeting',
26-
shortFlag: '-g',
27-
defaultValue: '',
28-
})
29-
.defaultHandler(({ options }) => {
30-
console.log(options.greeting);
25+
type CommonOptions = {
26+
verbose: boolean;
27+
};
28+
29+
const withCommonOptions = <O, G>(
30+
parser: Parser<O, G>,
31+
): Parser<O & CommonOptions, G> => {
32+
return parser.option('verbose', {
33+
longFlag: '--verbose',
34+
shortFlag: '-v',
35+
defaultValue: false,
3136
});
37+
};
38+
```
3239

33-
const parser = new Parser()
34-
.option('greeting', {
35-
longFlag: '--greeting',
36-
shortFlag: '-g',
37-
defaultValue: '',
38-
})
40+
2. Next, we can create the main and subparser. Both are created with the shared options. Although not shown, each parser can of course have its own options:
41+
42+
```ts
43+
const subparser = withCommonOptions(new Parser()).defaultHandler(
44+
({ options }) => {
45+
console.log(`[subparser] Verbose mode: ${options.verbose}`);
46+
},
47+
);
48+
49+
const parser = withCommonOptions(new Parser())
3950
.subparser('v2', {
4051
parser: subparser,
4152
description: 'Version 2 of this CLI',
4253
})
4354
.defaultHandler(({ options }) => {
44-
console.log(options.greeting);
55+
console.log(`[main parser] Verbose mode: ${options.verbose}`);
4556
});
4657
```
4758

4859
If we append `v2` to the arguments, the subparser will be called. If the first positional argument (here, `v2`) identifies a subparser, then only the subparser will be in charge of validating and parsing all further arguments. The main parser simply delegates the call to the subparser:
4960

5061
```ts
51-
// hello from the main parser
52-
parser.parse(['-g', 'hello from the main parser']).call();
53-
// hello from the subparser
54-
parser.parse(['v2', '-g', 'hello from the subparser']).call();
62+
// [main parser] Verbose mode: true
63+
parser.parse(['-v']).call();
64+
65+
// [subparser] Verbose mode: true
66+
parser.parse(['v2', '-v']).call();
5567
```
5668

69+
As you might have guessed by now, this example method of sharing global options is merely a way to **reduce code duplication**. Our goal was to have to define the `--verbose` option only once and use it in both the main and subparser.
70+
5771
## Caveats
5872

59-
Subparsers are a bit more complex to setup and cannot access their parent parser's options (see table above). Hence, you might need to setup globals repeatedly for each parser and have some duplicated code here and there. However, this is a small price to pay for the added flexibility and control over your app's structure.
73+
Subparsers are a bit more complex to setup and, by default, cannot access their parent parser's options. It is still possible, though, and in some scenarios a small price to pay for the added flexibility and control over your app's structure.

0 commit comments

Comments
 (0)