@flowscripter/dynamic-cli-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.github/workflows/check-bun-dependencies.yml +9 -0
  2. package/.github/workflows/lint-pr-message.yml +11 -0
  3. package/.github/workflows/release-bun-library.yml +8 -0
  4. package/.github/workflows/validate-bun-library-pr.yml +8 -0
  5. package/LICENSE +21 -0
  6. package/README.md +1475 -0
  7. package/bun.lock +63 -0
  8. package/index.ts +89 -0
  9. package/package.json +23 -0
  10. package/src/api/CLI.ts +17 -0
  11. package/src/api/CLIConfig.ts +19 -0
  12. package/src/api/Context.ts +25 -0
  13. package/src/api/RunResult.ts +142 -0
  14. package/src/api/argument/Argument.ts +36 -0
  15. package/src/api/argument/ArgumentValueTypes.ts +87 -0
  16. package/src/api/argument/ComplexOption.ts +31 -0
  17. package/src/api/argument/GlobalCommandArgument.ts +17 -0
  18. package/src/api/argument/Option.ts +32 -0
  19. package/src/api/argument/Positional.ts +27 -0
  20. package/src/api/argument/SubCommandArgument.ts +20 -0
  21. package/src/api/command/Command.ts +22 -0
  22. package/src/api/command/GlobalCommand.ts +33 -0
  23. package/src/api/command/GlobalModifierCommand.ts +12 -0
  24. package/src/api/command/GroupCommand.ts +20 -0
  25. package/src/api/command/SubCommand.ts +41 -0
  26. package/src/api/command/UsageExample.ts +21 -0
  27. package/src/api/service/ServiceProvider.ts +52 -0
  28. package/src/api/service/core/AsciiBannerGeneratorService.ts +35 -0
  29. package/src/api/service/core/KeyValueService.ts +28 -0
  30. package/src/api/service/core/PrinterService.ts +266 -0
  31. package/src/api/service/core/ShutdownService.ts +12 -0
  32. package/src/api/service/core/SyntaxHighlighterService.ts +42 -0
  33. package/src/cli/BaseCLI.ts +312 -0
  34. package/src/cli/DefaultRuntimeCLI.ts +54 -0
  35. package/src/command/MultiCommandCliHelpCommand.ts +394 -0
  36. package/src/command/SingleCommandCliHelpCommand.ts +183 -0
  37. package/src/command/UsageCommand.ts +35 -0
  38. package/src/command/VersionCommand.ts +22 -0
  39. package/src/launcher.ts +127 -0
  40. package/src/runtime/DefaultContext.ts +32 -0
  41. package/src/runtime/argument/ArgumentTypeGuards.ts +8 -0
  42. package/src/runtime/command/CommandTypeGuards.ts +30 -0
  43. package/src/runtime/command/CommandValidator.ts +549 -0
  44. package/src/runtime/parser.ts +219 -0
  45. package/src/runtime/registry/CommandRegistry.ts +121 -0
  46. package/src/runtime/registry/DefaultCommandRegistry.ts +318 -0
  47. package/src/runtime/registry/DefaultServiceProviderRegistry.ts +26 -0
  48. package/src/runtime/registry/ServiceProviderRegistry.ts +11 -0
  49. package/src/runtime/runner.ts +697 -0
  50. package/src/runtime/scanner.ts +417 -0
  51. package/src/runtime/values/ValuePopulationResult.ts +41 -0
  52. package/src/runtime/values/argumentValueMerge.ts +201 -0
  53. package/src/runtime/values/argumentValueValidation.ts +610 -0
  54. package/src/runtime/values/globalCommandValuePopulation.ts +96 -0
  55. package/src/runtime/values/subCommandValuePopulation.ts +680 -0
  56. package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +45 -0
  57. package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +38 -0
  58. package/src/service/asciiBannerGenerator/standard.flf.json +3 -0
  59. package/src/service/banner/BannerServiceProvider.ts +99 -0
  60. package/src/service/banner/command/NoBannerCommand.ts +42 -0
  61. package/src/service/configuration/ConfigurationServiceProvider.ts +571 -0
  62. package/src/service/configuration/DefaultKeyValueService.ts +60 -0
  63. package/src/service/configuration/command/ConfigCommand.ts +43 -0
  64. package/src/service/configuration/command/DumpConfigCommand.ts +47 -0
  65. package/src/service/printer/DefaultPrinterService.ts +256 -0
  66. package/src/service/printer/PrinterServiceProvider.ts +61 -0
  67. package/src/service/printer/command/DarkModeCommand.ts +43 -0
  68. package/src/service/printer/command/LogLevelCommand.ts +68 -0
  69. package/src/service/printer/command/NoColorCommand.ts +43 -0
  70. package/src/service/printer/terminal/Ansi.ts +10 -0
  71. package/src/service/printer/terminal/Color.ts +14 -0
  72. package/src/service/printer/terminal/Progress.ts +373 -0
  73. package/src/service/printer/terminal/Spinner.ts +100 -0
  74. package/src/service/printer/terminal/Styler.ts +5 -0
  75. package/src/service/printer/terminal/Terminal.ts +8 -0
  76. package/src/service/printer/terminal/Theme.ts +34 -0
  77. package/src/service/printer/terminal/TtyStyler.ts +39 -0
  78. package/src/service/printer/terminal/TtyTerminal.ts +65 -0
  79. package/src/service/shutdown/DefaultShutdownService.ts +9 -0
  80. package/src/service/shutdown/ShutdownServiceProvider.ts +55 -0
  81. package/src/service/syntaxHighlighter/DefaultSyntaxHighlighterService.ts +52 -0
  82. package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +51 -0
  83. package/src/util/configHelper.ts +95 -0
  84. package/src/util/envVarHelper.ts +338 -0
  85. package/src/util/helpHelper.ts +751 -0
  86. package/src/util/logger.ts +84 -0
  87. package/src/util/runnerHelper.ts +112 -0
  88. package/tests/cli/BaseCLI.test.ts +344 -0
  89. package/tests/command/MultiCommandCliHelpCommand.test.ts +650 -0
  90. package/tests/command/SingleCommandCliHelpCommand.test.ts +384 -0
  91. package/tests/command/UsageCommand.test.ts +53 -0
  92. package/tests/command/VersionCommand.test.ts +36 -0
  93. package/tests/fixtures/CLIConfig.ts +9 -0
  94. package/tests/fixtures/Command.ts +229 -0
  95. package/tests/fixtures/CommandRegistry.ts +8 -0
  96. package/tests/fixtures/ConfigurationServiceProvider.ts +28 -0
  97. package/tests/fixtures/Context.ts +33 -0
  98. package/tests/fixtures/ServiceProviderRegistry.ts +16 -0
  99. package/tests/fixtures/StreamString.ts +37 -0
  100. package/tests/fixtures/util.ts +41 -0
  101. package/tests/runtime/DefaultContext.test.ts +20 -0
  102. package/tests/runtime/argument/ArgumentTypeGuards.test.ts +49 -0
  103. package/tests/runtime/command/CommandTypeGuards.test.ts +43 -0
  104. package/tests/runtime/command/CommandValidator.test.ts +816 -0
  105. package/tests/runtime/parser.test.ts +766 -0
  106. package/tests/runtime/registry/DefaultCommandRegistry.test.ts +166 -0
  107. package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +42 -0
  108. package/tests/runtime/runner.test.ts +1298 -0
  109. package/tests/runtime/scanner.test.ts +1113 -0
  110. package/tests/runtime/values/argumentValueMerge.test.ts +314 -0
  111. package/tests/runtime/values/argumentValueValidation.test.ts +1020 -0
  112. package/tests/runtime/values/globalCommandValuePopulation.test.ts +662 -0
  113. package/tests/runtime/values/subCommandValuePopulation.test.ts +2658 -0
  114. package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +22 -0
  115. package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +70 -0
  116. package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +22 -0
  117. package/tests/service/asciiBannerGenerator/small.flf.json +3 -0
  118. package/tests/service/configuration/ConfigurationServiceProvider.test.ts +249 -0
  119. package/tests/service/configuration/DefaultKeyValueService.test.ts +65 -0
  120. package/tests/service/launchBanner/BannerServiceProvider.test.ts +49 -0
  121. package/tests/service/launchBanner/__snapshots__/BannerServiceProvider.test.ts.snap +3 -0
  122. package/tests/service/launchBanner/command/NoBannerCommand.test.ts +21 -0
  123. package/tests/service/printer/DefaultPrinterService.test.ts +409 -0
  124. package/tests/service/printer/PrinterServiceProvider.test.ts +29 -0
  125. package/tests/service/printer/command/LogLevelCommand.test.ts +58 -0
  126. package/tests/service/printer/terminal/Progress.test.ts +40 -0
  127. package/tests/service/printer/terminal/Spinner.test.ts +57 -0
  128. package/tests/service/shutdown/ShutdownServiceProvider.test.ts +13 -0
  129. package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +123 -0
  130. package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +41 -0
  131. package/tests/util/configHelper.test.ts +275 -0
  132. package/tests/util/envVarHelper.test.ts +318 -0
  133. package/tests/util/logger.test.ts +10 -0
@@ -0,0 +1,2658 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import populateSubCommandValues from "../../../src/runtime/values/subCommandValuePopulation.ts";
3
+ import {
4
+ ArgumentValueTypeName,
5
+ ComplexValueTypeName,
6
+ type PopulatedArgumentValues,
7
+ } from "../../../src/api/argument/ArgumentValueTypes.ts";
8
+ import type { SubCommandValuePopulationResult } from "../../../src/runtime/values/ValuePopulationResult.ts";
9
+ import { InvalidArgumentReason } from "../../../src/api/RunResult.ts";
10
+ import type SubCommand from "../../../src/api/command/SubCommand.ts";
11
+ import type ComplexOption from "../../../src/api/argument/ComplexOption.ts";
12
+
13
+ function expectExtractResult(
14
+ result: SubCommandValuePopulationResult,
15
+ values: PopulatedArgumentValues,
16
+ unusedArgs: ReadonlyArray<string>,
17
+ ) {
18
+ expect(result.populatedArgumentValues).toEqual(values);
19
+ expect(result.unusedArgs).toEqual(unusedArgs);
20
+ }
21
+ describe("subCommandValuePopulation Tests", () => {
22
+ test("Option argument", () => {
23
+ const command: SubCommand = {
24
+ name: "command",
25
+ options: [{
26
+ name: "foo",
27
+ type: ArgumentValueTypeName.STRING,
28
+ shortAlias: "f",
29
+ }],
30
+ positionals: [],
31
+ execute: async (): Promise<void> => {
32
+ },
33
+ };
34
+
35
+ let result = populateSubCommandValues(
36
+ command,
37
+ ["--foo", "bar"],
38
+ undefined,
39
+ );
40
+ expectExtractResult(result, { foo: "bar" }, []);
41
+ expect(result.invalidArgument).toBeUndefined();
42
+
43
+ result = populateSubCommandValues(
44
+ command,
45
+ ["--foo=bar"],
46
+ undefined,
47
+ );
48
+ expectExtractResult(result, { foo: "bar" }, []);
49
+ expect(result.invalidArgument).toBeUndefined();
50
+
51
+ result = populateSubCommandValues(
52
+ command,
53
+ ["-f", "bar"],
54
+ undefined,
55
+ );
56
+ expectExtractResult(result, { foo: "bar" }, []);
57
+ expect(result.invalidArgument).toBeUndefined();
58
+
59
+ result = populateSubCommandValues(
60
+ command,
61
+ ["-f=bar"],
62
+ undefined,
63
+ );
64
+ expectExtractResult(result, { foo: "bar" }, []);
65
+ expect(result.invalidArgument).toBeUndefined();
66
+
67
+ result = populateSubCommandValues(
68
+ command,
69
+ ["--foo", "goo=1 and goo=2"],
70
+ undefined,
71
+ );
72
+ expectExtractResult(result, { foo: "goo=1 and goo=2" }, []);
73
+ expect(result.invalidArgument).toBeUndefined();
74
+
75
+ result = populateSubCommandValues(
76
+ command,
77
+ ["--foo=goo=1 and goo=2"],
78
+ undefined,
79
+ );
80
+ expectExtractResult(result, { foo: "goo=1 and goo=2" }, []);
81
+ expect(result.invalidArgument).toBeUndefined();
82
+ });
83
+
84
+ test("Option types", () => {
85
+ let command: SubCommand = {
86
+ name: "command",
87
+ options: [{
88
+ name: "foo",
89
+ shortAlias: "f",
90
+ type: ArgumentValueTypeName.NUMBER,
91
+ }],
92
+ positionals: [],
93
+ execute: async (): Promise<void> => {
94
+ },
95
+ };
96
+
97
+ let result = populateSubCommandValues(
98
+ command,
99
+ ["--foo", "1.1"],
100
+ undefined,
101
+ );
102
+ expectExtractResult(result, { foo: "1.1" }, []);
103
+ expect(result.invalidArgument).toBeUndefined();
104
+
105
+ command = {
106
+ name: "command",
107
+ options: [{
108
+ name: "foo",
109
+ shortAlias: "f",
110
+ type: ArgumentValueTypeName.INTEGER,
111
+ }],
112
+ positionals: [],
113
+ execute: async (): Promise<void> => {
114
+ },
115
+ };
116
+
117
+ result = populateSubCommandValues(
118
+ command,
119
+ ["-f", "1"],
120
+ undefined,
121
+ );
122
+ expectExtractResult(result, { foo: "1" }, []);
123
+ expect(result.invalidArgument).toBeUndefined();
124
+
125
+ command = {
126
+ name: "command",
127
+ options: [{
128
+ name: "foo",
129
+ shortAlias: "f",
130
+ type: ArgumentValueTypeName.SECRET,
131
+ }],
132
+ positionals: [],
133
+ execute: async (): Promise<void> => {
134
+ },
135
+ };
136
+
137
+ result = populateSubCommandValues(
138
+ command,
139
+ ["-f", "xxx"],
140
+ undefined,
141
+ );
142
+ expectExtractResult(result, { foo: "xxx" }, []);
143
+ expect(result.invalidArgument).toBeUndefined();
144
+
145
+ command = {
146
+ name: "command",
147
+ options: [{
148
+ name: "foo",
149
+ shortAlias: "f",
150
+ type: ArgumentValueTypeName.STRING,
151
+ }],
152
+ positionals: [],
153
+ execute: async (): Promise<void> => {
154
+ },
155
+ };
156
+
157
+ result = populateSubCommandValues(
158
+ command,
159
+ ["-f", "bar"],
160
+ undefined,
161
+ );
162
+ expectExtractResult(result, { foo: "bar" }, []);
163
+ expect(result.invalidArgument).toBeUndefined();
164
+
165
+ command = {
166
+ name: "command",
167
+ options: [{
168
+ name: "foo",
169
+ shortAlias: "f",
170
+ type: ArgumentValueTypeName.BOOLEAN,
171
+ }],
172
+ positionals: [],
173
+ execute: async (): Promise<void> => {
174
+ },
175
+ };
176
+
177
+ result = populateSubCommandValues(
178
+ command,
179
+ ["-f"],
180
+ undefined,
181
+ );
182
+ expectExtractResult(result, { foo: "true" }, []);
183
+ expect(result.invalidArgument).toBeUndefined();
184
+ });
185
+
186
+ test("Option array", () => {
187
+ const command: SubCommand = {
188
+ name: "command",
189
+ options: [{
190
+ name: "foo",
191
+ type: ArgumentValueTypeName.STRING,
192
+ shortAlias: "f",
193
+ isArray: true,
194
+ }],
195
+ positionals: [],
196
+ execute: async (): Promise<void> => {
197
+ },
198
+ };
199
+
200
+ let result = populateSubCommandValues(
201
+ command,
202
+ ["--foo", "bar"],
203
+ undefined,
204
+ );
205
+ expectExtractResult(result, { foo: ["bar"] }, []);
206
+ expect(result.invalidArgument).toBeUndefined();
207
+
208
+ result = populateSubCommandValues(
209
+ command,
210
+ ["--foo", "bar", "--foo", "bar"],
211
+ undefined,
212
+ );
213
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
214
+ expect(result.invalidArgument).toBeUndefined();
215
+
216
+ result = populateSubCommandValues(
217
+ command,
218
+ ["--foo=bar", "--foo", "bar"],
219
+ undefined,
220
+ );
221
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
222
+ expect(result.invalidArgument).toBeUndefined();
223
+
224
+ result = populateSubCommandValues(
225
+ command,
226
+ ["--foo=bar", "-f", "bar"],
227
+ undefined,
228
+ );
229
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
230
+ expect(result.invalidArgument).toBeUndefined();
231
+
232
+ result = populateSubCommandValues(
233
+ command,
234
+ ["--foo=bar", "-f=bar"],
235
+ undefined,
236
+ );
237
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
238
+ expect(result.invalidArgument).toBeUndefined();
239
+ });
240
+
241
+ test("Option argument configured value is overridden by arg", () => {
242
+ let command: SubCommand = {
243
+ name: "command",
244
+ options: [{
245
+ name: "foo",
246
+ type: ArgumentValueTypeName.STRING,
247
+ }],
248
+ positionals: [],
249
+ execute: async (): Promise<void> => {
250
+ },
251
+ };
252
+
253
+ let result = populateSubCommandValues(
254
+ command,
255
+ ["--foo", "bar"],
256
+ { foo: "nope" },
257
+ );
258
+ expectExtractResult(result, { foo: "bar" }, []);
259
+ expect(result.invalidArgument).toBeUndefined();
260
+
261
+ command = {
262
+ name: "command",
263
+ options: [{
264
+ name: "foo",
265
+ type: ArgumentValueTypeName.NUMBER,
266
+ }],
267
+ positionals: [],
268
+ execute: async (): Promise<void> => {
269
+ },
270
+ };
271
+
272
+ result = populateSubCommandValues(
273
+ command,
274
+ ["--foo", "1"],
275
+ { foo: 2 },
276
+ );
277
+ expectExtractResult(result, { foo: "1" }, []);
278
+ expect(result.invalidArgument).toBeUndefined();
279
+
280
+ command = {
281
+ name: "command",
282
+ options: [{
283
+ name: "foo",
284
+ type: ArgumentValueTypeName.BOOLEAN,
285
+ }],
286
+ positionals: [],
287
+ execute: async (): Promise<void> => {
288
+ },
289
+ };
290
+
291
+ result = populateSubCommandValues(
292
+ command,
293
+ ["--foo", "true"],
294
+ { foo: false },
295
+ );
296
+ expectExtractResult(result, { foo: "true" }, []);
297
+ expect(result.invalidArgument).toBeUndefined();
298
+ });
299
+
300
+ test("Option argument default value is overridden by configured value", () => {
301
+ let command: SubCommand = {
302
+ name: "command",
303
+ options: [{
304
+ name: "foo",
305
+ type: ArgumentValueTypeName.STRING,
306
+ defaultValue: "nope",
307
+ }],
308
+ positionals: [],
309
+ execute: async (): Promise<void> => {
310
+ },
311
+ };
312
+
313
+ let result = populateSubCommandValues(
314
+ command,
315
+ [],
316
+ { foo: "bar" },
317
+ );
318
+ expectExtractResult(result, { foo: "bar" }, []);
319
+ expect(result.invalidArgument).toBeUndefined();
320
+
321
+ command = {
322
+ name: "command",
323
+ options: [{
324
+ name: "foo",
325
+ type: ArgumentValueTypeName.NUMBER,
326
+ defaultValue: 2,
327
+ }],
328
+ positionals: [],
329
+ execute: async (): Promise<void> => {
330
+ },
331
+ };
332
+
333
+ result = populateSubCommandValues(
334
+ command,
335
+ [],
336
+ { foo: 1 },
337
+ );
338
+ expectExtractResult(result, { foo: 1 }, []);
339
+ expect(result.invalidArgument).toBeUndefined();
340
+
341
+ command = {
342
+ name: "command",
343
+ options: [{
344
+ name: "foo",
345
+ type: ArgumentValueTypeName.BOOLEAN,
346
+ defaultValue: false,
347
+ }],
348
+ positionals: [],
349
+ execute: async (): Promise<void> => {
350
+ },
351
+ };
352
+
353
+ result = populateSubCommandValues(
354
+ command,
355
+ [],
356
+ { foo: true },
357
+ );
358
+ expectExtractResult(result, { foo: true }, []);
359
+ expect(result.invalidArgument).toBeUndefined();
360
+ });
361
+
362
+ test("Option argument default value is overridden by arg", () => {
363
+ let command: SubCommand = {
364
+ name: "command",
365
+ options: [{
366
+ name: "foo",
367
+ type: ArgumentValueTypeName.STRING,
368
+ defaultValue: "nope",
369
+ }],
370
+ positionals: [],
371
+ execute: async (): Promise<void> => {
372
+ },
373
+ };
374
+
375
+ let result = populateSubCommandValues(
376
+ command,
377
+ ["--foo", "bar"],
378
+ undefined,
379
+ );
380
+ expectExtractResult(result, { foo: "bar" }, []);
381
+ expect(result.invalidArgument).toBeUndefined();
382
+
383
+ command = {
384
+ name: "command",
385
+ options: [{
386
+ name: "foo",
387
+ type: ArgumentValueTypeName.NUMBER,
388
+ defaultValue: 2,
389
+ }],
390
+ positionals: [],
391
+ execute: async (): Promise<void> => {
392
+ },
393
+ };
394
+
395
+ result = populateSubCommandValues(
396
+ command,
397
+ ["--foo", "1"],
398
+ undefined,
399
+ );
400
+ expectExtractResult(result, { foo: "1" }, []);
401
+ expect(result.invalidArgument).toBeUndefined();
402
+
403
+ command = {
404
+ name: "command",
405
+ options: [{
406
+ name: "foo",
407
+ type: ArgumentValueTypeName.BOOLEAN,
408
+ defaultValue: true,
409
+ }],
410
+ positionals: [],
411
+ execute: async (): Promise<void> => {
412
+ },
413
+ };
414
+
415
+ result = populateSubCommandValues(
416
+ command,
417
+ ["--foo", "false"],
418
+ undefined,
419
+ );
420
+ expectExtractResult(result, { foo: "false" }, []);
421
+ expect(result.invalidArgument).toBeUndefined();
422
+ });
423
+
424
+ test("Option argument configured value is used if no arg provided", () => {
425
+ let command: SubCommand = {
426
+ name: "command",
427
+ options: [{
428
+ name: "foo",
429
+ type: ArgumentValueTypeName.STRING,
430
+ }],
431
+ positionals: [],
432
+ execute: async (): Promise<void> => {
433
+ },
434
+ };
435
+
436
+ let result = populateSubCommandValues(
437
+ command,
438
+ [],
439
+ { foo: "bar" },
440
+ );
441
+ expectExtractResult(result, { foo: "bar" }, []);
442
+ expect(result.invalidArgument).toBeUndefined();
443
+
444
+ command = {
445
+ name: "command",
446
+ options: [{
447
+ name: "foo",
448
+ type: ArgumentValueTypeName.NUMBER,
449
+ }],
450
+ positionals: [],
451
+ execute: async (): Promise<void> => {
452
+ },
453
+ };
454
+
455
+ result = populateSubCommandValues(
456
+ command,
457
+ [],
458
+ { foo: 1 },
459
+ );
460
+ expectExtractResult(result, { foo: 1 }, []);
461
+ expect(result.invalidArgument).toBeUndefined();
462
+
463
+ command = {
464
+ name: "command",
465
+ options: [{
466
+ name: "foo",
467
+ type: ArgumentValueTypeName.BOOLEAN,
468
+ }],
469
+ positionals: [],
470
+ execute: async (): Promise<void> => {
471
+ },
472
+ };
473
+
474
+ result = populateSubCommandValues(
475
+ command,
476
+ [],
477
+ { foo: false },
478
+ );
479
+ expectExtractResult(result, { foo: false }, []);
480
+ expect(result.invalidArgument).toBeUndefined();
481
+ });
482
+
483
+ test("Two options", () => {
484
+ let command: SubCommand = {
485
+ name: "command",
486
+ options: [{
487
+ name: "foo",
488
+ type: ArgumentValueTypeName.STRING,
489
+ isArray: false,
490
+ }, {
491
+ name: "goo",
492
+ type: ArgumentValueTypeName.STRING,
493
+ }],
494
+ positionals: [],
495
+ execute: async (): Promise<void> => {
496
+ },
497
+ };
498
+
499
+ let result = populateSubCommandValues(
500
+ command,
501
+ ["--foo", "bar", "--goo=gar"],
502
+ undefined,
503
+ );
504
+ expectExtractResult(result, { foo: "bar", goo: "gar" }, []);
505
+ expect(result.invalidArgument).toBeUndefined();
506
+
507
+ command = {
508
+ name: "command",
509
+ options: [{
510
+ name: "foo",
511
+ type: ArgumentValueTypeName.BOOLEAN,
512
+ isArray: false,
513
+ }, {
514
+ name: "goo",
515
+ type: ArgumentValueTypeName.STRING,
516
+ }],
517
+ positionals: [],
518
+ execute: async (): Promise<void> => {
519
+ },
520
+ };
521
+
522
+ result = populateSubCommandValues(
523
+ command,
524
+ ["--foo", "--goo=gar"],
525
+ undefined,
526
+ );
527
+ expectExtractResult(result, { foo: "true", goo: "gar" }, []);
528
+ expect(result.invalidArgument).toBeUndefined();
529
+
530
+ command = {
531
+ name: "command",
532
+ options: [{
533
+ name: "foo",
534
+ type: ArgumentValueTypeName.BOOLEAN,
535
+ isArray: true,
536
+ }, {
537
+ name: "goo",
538
+ type: ArgumentValueTypeName.STRING,
539
+ }],
540
+ positionals: [],
541
+ execute: async (): Promise<void> => {
542
+ },
543
+ };
544
+
545
+ result = populateSubCommandValues(
546
+ command,
547
+ [
548
+ "--foo",
549
+ "--foo",
550
+ "false",
551
+ "--goo=gar",
552
+ ],
553
+ undefined,
554
+ );
555
+ expectExtractResult(result, { foo: ["true", "false"], goo: "gar" }, []);
556
+ expect(result.invalidArgument).toBeUndefined();
557
+ });
558
+
559
+ test("Interleaved option arrays", () => {
560
+ const command: SubCommand = {
561
+ name: "command",
562
+ options: [{
563
+ name: "foo",
564
+ type: ArgumentValueTypeName.STRING,
565
+ isArray: true,
566
+ }, {
567
+ name: "goo",
568
+ type: ArgumentValueTypeName.BOOLEAN,
569
+ isArray: true,
570
+ shortAlias: "g",
571
+ }],
572
+ positionals: [],
573
+ execute: async (): Promise<void> => {
574
+ },
575
+ };
576
+
577
+ let result = populateSubCommandValues(
578
+ command,
579
+ [
580
+ "--foo",
581
+ "bar1",
582
+ "--goo=true",
583
+ "--foo",
584
+ "bar2",
585
+ "--goo=false",
586
+ ],
587
+ undefined,
588
+ );
589
+ expectExtractResult(
590
+ result,
591
+ { foo: ["bar1", "bar2"], goo: ["true", "false"] },
592
+ [],
593
+ );
594
+ expect(result.invalidArgument).toBeUndefined();
595
+
596
+ result = populateSubCommandValues(
597
+ command,
598
+ [
599
+ "--foo",
600
+ "bar1",
601
+ "--goo=true",
602
+ "--goo=false",
603
+ "--foo",
604
+ "bar2",
605
+ ],
606
+ undefined,
607
+ );
608
+ expectExtractResult(
609
+ result,
610
+ { foo: ["bar1", "bar2"], goo: ["true", "false"] },
611
+ [],
612
+ );
613
+ expect(result.invalidArgument).toBeUndefined();
614
+
615
+ result = populateSubCommandValues(
616
+ command,
617
+ [
618
+ "--foo",
619
+ "bar1",
620
+ "-g",
621
+ "--foo",
622
+ "bar2",
623
+ "-g=false",
624
+ ],
625
+ undefined,
626
+ );
627
+ expectExtractResult(
628
+ result,
629
+ { foo: ["bar1", "bar2"], goo: ["true", "false"] },
630
+ [],
631
+ );
632
+ expect(result.invalidArgument).toBeUndefined();
633
+ });
634
+
635
+ test("Unknown option name", () => {
636
+ const command: SubCommand = {
637
+ name: "command",
638
+ options: [{
639
+ name: "foo",
640
+ type: ArgumentValueTypeName.STRING,
641
+ }],
642
+ positionals: [],
643
+ execute: async (): Promise<void> => {
644
+ },
645
+ };
646
+
647
+ let result = populateSubCommandValues(
648
+ command,
649
+ ["--goo=moo"],
650
+ undefined,
651
+ );
652
+ expectExtractResult(result, {}, ["--goo=moo"]);
653
+ expect(result.invalidArgument).toBeUndefined();
654
+
655
+ result = populateSubCommandValues(
656
+ command,
657
+ ["--goo", "moo"],
658
+ undefined,
659
+ );
660
+ expectExtractResult(result, {}, ["--goo", "moo"]);
661
+ expect(result.invalidArgument).toBeUndefined();
662
+ });
663
+
664
+ test("Illegal option syntax", () => {
665
+ const command: SubCommand = {
666
+ name: "command",
667
+ options: [{
668
+ name: "foo",
669
+ shortAlias: "f",
670
+ type: ArgumentValueTypeName.STRING,
671
+ }],
672
+ positionals: [],
673
+ execute: async (): Promise<void> => {
674
+ },
675
+ };
676
+
677
+ let result = populateSubCommandValues(
678
+ command,
679
+ ["-foo=moo"],
680
+ undefined,
681
+ );
682
+ expectExtractResult(result, {}, ["-foo=moo"]);
683
+ expect(result.invalidArgument).toBeUndefined();
684
+
685
+ result = populateSubCommandValues(
686
+ command,
687
+ ["--f", "moo"],
688
+ undefined,
689
+ );
690
+ expectExtractResult(result, {}, ["--f", "moo"]);
691
+ expect(result.invalidArgument).toBeUndefined();
692
+
693
+ result = populateSubCommandValues(
694
+ command,
695
+ ["--foo=", "moo"],
696
+ undefined,
697
+ );
698
+ expectExtractResult(result, {}, ["moo"]);
699
+ expect(result.invalidArgument).toEqual({
700
+ name: "foo",
701
+ reason: InvalidArgumentReason.MISSING_VALUE,
702
+ });
703
+
704
+ result = populateSubCommandValues(
705
+ command,
706
+ ["-f=", "moo"],
707
+ undefined,
708
+ );
709
+ expectExtractResult(result, {}, ["moo"]);
710
+ expect(result.invalidArgument).toEqual({
711
+ name: "f",
712
+ reason: InvalidArgumentReason.MISSING_VALUE,
713
+ });
714
+ });
715
+
716
+ test("Positional argument", () => {
717
+ let command: SubCommand = {
718
+ name: "command",
719
+ options: [],
720
+ positionals: [{
721
+ name: "foo",
722
+ type: ArgumentValueTypeName.STRING,
723
+ }],
724
+ execute: async (): Promise<void> => {
725
+ },
726
+ };
727
+
728
+ let result = populateSubCommandValues(
729
+ command,
730
+ ["bar"],
731
+ undefined,
732
+ );
733
+ expectExtractResult(result, { foo: "bar" }, []);
734
+ expect(result.invalidArgument).toBeUndefined();
735
+
736
+ command = {
737
+ name: "command",
738
+ options: [],
739
+ positionals: [{
740
+ name: "foo",
741
+ type: ArgumentValueTypeName.STRING,
742
+ }, {
743
+ name: "bar",
744
+ type: ArgumentValueTypeName.STRING,
745
+ }],
746
+ execute: async (): Promise<void> => {
747
+ },
748
+ };
749
+
750
+ result = populateSubCommandValues(
751
+ command,
752
+ ["foo", "bar"],
753
+ undefined,
754
+ );
755
+ expectExtractResult(result, { foo: "foo", bar: "bar" }, []);
756
+ expect(result.invalidArgument).toBeUndefined();
757
+ });
758
+
759
+ test("Positional types", () => {
760
+ let command: SubCommand = {
761
+ name: "command",
762
+ options: [],
763
+ positionals: [{
764
+ name: "foo",
765
+ type: ArgumentValueTypeName.NUMBER,
766
+ }],
767
+ execute: async (): Promise<void> => {
768
+ },
769
+ };
770
+
771
+ let result = populateSubCommandValues(
772
+ command,
773
+ ["1"],
774
+ undefined,
775
+ );
776
+ expectExtractResult(result, { foo: "1" }, []);
777
+ expect(result.invalidArgument).toBeUndefined();
778
+
779
+ command = {
780
+ name: "command",
781
+ options: [],
782
+ positionals: [{
783
+ name: "foo",
784
+ type: ArgumentValueTypeName.STRING,
785
+ }],
786
+ execute: async (): Promise<void> => {
787
+ },
788
+ };
789
+ result = populateSubCommandValues(
790
+ command,
791
+ ["bar"],
792
+ undefined,
793
+ );
794
+ expectExtractResult(result, { foo: "bar" }, []);
795
+ expect(result.invalidArgument).toBeUndefined();
796
+
797
+ command = {
798
+ name: "command",
799
+ options: [],
800
+ positionals: [{
801
+ name: "foo",
802
+ type: ArgumentValueTypeName.BOOLEAN,
803
+ }],
804
+ execute: async (): Promise<void> => {
805
+ },
806
+ };
807
+ result = populateSubCommandValues(
808
+ command,
809
+ ["true"],
810
+ undefined,
811
+ );
812
+ expectExtractResult(result, { foo: "true" }, []);
813
+ expect(result.invalidArgument).toBeUndefined();
814
+ });
815
+
816
+ test("Multiple positionals", () => {
817
+ const command: SubCommand = {
818
+ name: "command",
819
+ options: [],
820
+ positionals: [{
821
+ name: "foo1",
822
+ type: ArgumentValueTypeName.STRING,
823
+ }, {
824
+ name: "foo2",
825
+ type: ArgumentValueTypeName.STRING,
826
+ }],
827
+ execute: async (): Promise<void> => {
828
+ },
829
+ };
830
+
831
+ const result = populateSubCommandValues(
832
+ command,
833
+ ["f1", "f2"],
834
+ undefined,
835
+ );
836
+ expectExtractResult(result, { foo1: "f1", foo2: "f2" }, []);
837
+ expect(result.invalidArgument).toBeUndefined();
838
+ });
839
+
840
+ test("Positional varargs", () => {
841
+ const command: SubCommand = {
842
+ name: "command",
843
+ options: [],
844
+ positionals: [{
845
+ name: "foo",
846
+ type: ArgumentValueTypeName.STRING,
847
+ isVarargMultiple: true,
848
+ isVarargOptional: true,
849
+ }],
850
+ execute: async (): Promise<void> => {
851
+ },
852
+ };
853
+
854
+ let result = populateSubCommandValues(
855
+ command,
856
+ ["bar", "bar"],
857
+ undefined,
858
+ );
859
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
860
+ expect(result.invalidArgument).toBeUndefined();
861
+
862
+ result = populateSubCommandValues(
863
+ command,
864
+ ["bar"],
865
+ undefined,
866
+ );
867
+ expectExtractResult(result, { foo: ["bar"] }, []);
868
+ expect(result.invalidArgument).toBeUndefined();
869
+
870
+ result = populateSubCommandValues(command, [], undefined);
871
+ expectExtractResult(result, {}, []);
872
+ expect(result.invalidArgument).toBeUndefined();
873
+ });
874
+
875
+ test("Multiple positional and varargs", () => {
876
+ const command: SubCommand = {
877
+ name: "command",
878
+ options: [],
879
+ positionals: [{
880
+ name: "foo",
881
+ type: ArgumentValueTypeName.STRING,
882
+ }, {
883
+ name: "bar",
884
+ type: ArgumentValueTypeName.STRING,
885
+ isVarargMultiple: true,
886
+ isVarargOptional: true,
887
+ }],
888
+ execute: async (): Promise<void> => {
889
+ },
890
+ };
891
+
892
+ let result = populateSubCommandValues(
893
+ command,
894
+ ["foo"],
895
+ undefined,
896
+ );
897
+ expectExtractResult(result, { foo: "foo" }, []);
898
+ expect(result.invalidArgument).toBeUndefined();
899
+
900
+ result = populateSubCommandValues(
901
+ command,
902
+ ["foo", "bar"],
903
+ undefined,
904
+ );
905
+ expectExtractResult(result, { foo: "foo", bar: ["bar"] }, []);
906
+ expect(result.invalidArgument).toBeUndefined();
907
+
908
+ result = populateSubCommandValues(
909
+ command,
910
+ ["foo", "bar", "bar"],
911
+ undefined,
912
+ );
913
+ expectExtractResult(result, { foo: "foo", bar: ["bar", "bar"] }, []);
914
+ expect(result.invalidArgument).toBeUndefined();
915
+
916
+ result = populateSubCommandValues(
917
+ command,
918
+ ["foo"],
919
+ undefined,
920
+ );
921
+ expectExtractResult(result, { foo: "foo" }, []);
922
+ expect(result.invalidArgument).toBeUndefined();
923
+ });
924
+
925
+ test("String options either side of positionals", () => {
926
+ const command: SubCommand = {
927
+ name: "command",
928
+ options: [{
929
+ name: "goo1",
930
+ type: ArgumentValueTypeName.STRING,
931
+ shortAlias: "1",
932
+ }, {
933
+ name: "goo2",
934
+ type: ArgumentValueTypeName.STRING,
935
+ shortAlias: "2",
936
+ }],
937
+ positionals: [{
938
+ name: "foo1",
939
+ type: ArgumentValueTypeName.STRING,
940
+ }, {
941
+ name: "foo2",
942
+ type: ArgumentValueTypeName.STRING,
943
+ }],
944
+ execute: async (): Promise<void> => {
945
+ },
946
+ };
947
+
948
+ let result = populateSubCommandValues(
949
+ command,
950
+ [
951
+ "--goo1=g1",
952
+ "f1",
953
+ "f2",
954
+ "--goo2=g2",
955
+ ],
956
+ undefined,
957
+ );
958
+ expectExtractResult(result, {
959
+ goo1: "g1",
960
+ goo2: "g2",
961
+ foo1: "f1",
962
+ foo2: "f2",
963
+ }, []);
964
+ expect(result.invalidArgument).toBeUndefined();
965
+
966
+ result = populateSubCommandValues(
967
+ command,
968
+ [
969
+ "--goo1",
970
+ "g1",
971
+ "f1",
972
+ "f2",
973
+ "--goo2",
974
+ "g2",
975
+ ],
976
+ undefined,
977
+ );
978
+ expectExtractResult(result, {
979
+ goo1: "g1",
980
+ goo2: "g2",
981
+ foo1: "f1",
982
+ foo2: "f2",
983
+ }, []);
984
+ expect(result.invalidArgument).toBeUndefined();
985
+
986
+ result = populateSubCommandValues(
987
+ command,
988
+ [
989
+ "-1",
990
+ "g1",
991
+ "f1",
992
+ "f2",
993
+ "-2",
994
+ "g2",
995
+ ],
996
+ undefined,
997
+ );
998
+ expectExtractResult(result, {
999
+ goo1: "g1",
1000
+ goo2: "g2",
1001
+ foo1: "f1",
1002
+ foo2: "f2",
1003
+ }, []);
1004
+ expect(result.invalidArgument).toBeUndefined();
1005
+ });
1006
+
1007
+ test("Boolean options either side of positionals", () => {
1008
+ const command: SubCommand = {
1009
+ name: "command",
1010
+ options: [{
1011
+ name: "goo1",
1012
+ shortAlias: "1",
1013
+ type: ArgumentValueTypeName.BOOLEAN,
1014
+ }, {
1015
+ name: "goo2",
1016
+ shortAlias: "2",
1017
+ type: ArgumentValueTypeName.BOOLEAN,
1018
+ }],
1019
+ positionals: [{
1020
+ name: "foo1",
1021
+ type: ArgumentValueTypeName.STRING,
1022
+ }, {
1023
+ name: "foo2",
1024
+ type: ArgumentValueTypeName.STRING,
1025
+ }],
1026
+ execute: async (): Promise<void> => {
1027
+ },
1028
+ };
1029
+
1030
+ let result = populateSubCommandValues(
1031
+ command,
1032
+ [
1033
+ "--goo1",
1034
+ "f1",
1035
+ "f2",
1036
+ "--goo2",
1037
+ ],
1038
+ undefined,
1039
+ );
1040
+ expectExtractResult(result, {
1041
+ goo1: "true",
1042
+ goo2: "true",
1043
+ foo1: "f1",
1044
+ foo2: "f2",
1045
+ }, []);
1046
+ expect(result.invalidArgument).toBeUndefined();
1047
+
1048
+ result = populateSubCommandValues(
1049
+ command,
1050
+ [
1051
+ "--goo1",
1052
+ "true",
1053
+ "f1",
1054
+ "f2",
1055
+ "--goo2",
1056
+ "false",
1057
+ ],
1058
+ undefined,
1059
+ );
1060
+ expectExtractResult(result, {
1061
+ goo1: "true",
1062
+ goo2: "false",
1063
+ foo1: "f1",
1064
+ foo2: "f2",
1065
+ }, []);
1066
+ expect(result.invalidArgument).toBeUndefined();
1067
+
1068
+ result = populateSubCommandValues(
1069
+ command,
1070
+ [
1071
+ "-1",
1072
+ "true",
1073
+ "f1",
1074
+ "f2",
1075
+ "-2",
1076
+ "false",
1077
+ ],
1078
+ undefined,
1079
+ );
1080
+ expectExtractResult(result, {
1081
+ goo1: "true",
1082
+ goo2: "false",
1083
+ foo1: "f1",
1084
+ foo2: "f2",
1085
+ }, []);
1086
+ expect(result.invalidArgument).toBeUndefined();
1087
+ });
1088
+
1089
+ test("Option array either side of positional", () => {
1090
+ let command: SubCommand = {
1091
+ name: "command",
1092
+ options: [{
1093
+ name: "foo",
1094
+ isArray: true,
1095
+ type: ArgumentValueTypeName.STRING,
1096
+ }],
1097
+ positionals: [{
1098
+ name: "goo",
1099
+ type: ArgumentValueTypeName.STRING,
1100
+ }],
1101
+ execute: async (): Promise<void> => {
1102
+ },
1103
+ };
1104
+
1105
+ let result = populateSubCommandValues(
1106
+ command,
1107
+ [
1108
+ "--foo",
1109
+ "f1",
1110
+ "goo1",
1111
+ "--foo",
1112
+ "f2",
1113
+ ],
1114
+ undefined,
1115
+ );
1116
+ expectExtractResult(result, {
1117
+ foo: ["f1", "f2"],
1118
+ goo: "goo1",
1119
+ }, []);
1120
+ expect(result.invalidArgument).toBeUndefined();
1121
+
1122
+ command = {
1123
+ name: "command",
1124
+ options: [{
1125
+ name: "foo",
1126
+ isArray: true,
1127
+ type: ArgumentValueTypeName.BOOLEAN,
1128
+ }],
1129
+ positionals: [{
1130
+ name: "goo",
1131
+ type: ArgumentValueTypeName.STRING,
1132
+ }],
1133
+ execute: async (): Promise<void> => {
1134
+ },
1135
+ };
1136
+ result = populateSubCommandValues(
1137
+ command,
1138
+ [
1139
+ "--foo",
1140
+ "true",
1141
+ "goo1",
1142
+ "--foo",
1143
+ "false",
1144
+ ],
1145
+ undefined,
1146
+ );
1147
+ expectExtractResult(result, {
1148
+ foo: ["true", "false"],
1149
+ goo: "goo1",
1150
+ }, []);
1151
+ expect(result.invalidArgument).toBeUndefined();
1152
+
1153
+ result = populateSubCommandValues(
1154
+ command,
1155
+ ["--foo", "goo1", "--foo"],
1156
+ undefined,
1157
+ );
1158
+ expectExtractResult(result, {
1159
+ foo: ["true", "true"],
1160
+ goo: "goo1",
1161
+ }, []);
1162
+ expect(result.invalidArgument).toBeUndefined();
1163
+ });
1164
+
1165
+ test("Unused option after used option", () => {
1166
+ let command: SubCommand = {
1167
+ name: "command",
1168
+ options: [{
1169
+ name: "foo",
1170
+ shortAlias: "f",
1171
+ type: ArgumentValueTypeName.STRING,
1172
+ }],
1173
+ positionals: [],
1174
+ execute: async (): Promise<void> => {
1175
+ },
1176
+ };
1177
+
1178
+ let result = populateSubCommandValues(
1179
+ command,
1180
+ [
1181
+ "--foo",
1182
+ "bar",
1183
+ "--goo",
1184
+ "gar",
1185
+ ],
1186
+ undefined,
1187
+ );
1188
+ expectExtractResult(result, { foo: "bar" }, ["--goo", "gar"]);
1189
+ expect(result.invalidArgument).toBeUndefined();
1190
+
1191
+ result = populateSubCommandValues(
1192
+ command,
1193
+ ["--foo=bar", "--goo=gar"],
1194
+ undefined,
1195
+ );
1196
+ expectExtractResult(result, { foo: "bar" }, ["--goo=gar"]);
1197
+ expect(result.invalidArgument).toBeUndefined();
1198
+
1199
+ command = {
1200
+ name: "command",
1201
+ options: [{
1202
+ name: "foo",
1203
+ shortAlias: "f",
1204
+ type: ArgumentValueTypeName.BOOLEAN,
1205
+ }],
1206
+ positionals: [],
1207
+ execute: async (): Promise<void> => {
1208
+ },
1209
+ };
1210
+
1211
+ result = populateSubCommandValues(
1212
+ command,
1213
+ ["-f", "--goo", "gar"],
1214
+ undefined,
1215
+ );
1216
+ expectExtractResult(result, { foo: "true" }, ["--goo", "gar"]);
1217
+ expect(result.invalidArgument).toBeUndefined();
1218
+ });
1219
+
1220
+ test("Unused option before used option", () => {
1221
+ let command: SubCommand = {
1222
+ name: "command",
1223
+ options: [{
1224
+ name: "foo",
1225
+ shortAlias: "f",
1226
+ type: ArgumentValueTypeName.STRING,
1227
+ }],
1228
+ positionals: [],
1229
+ execute: async (): Promise<void> => {
1230
+ },
1231
+ };
1232
+
1233
+ let result = populateSubCommandValues(
1234
+ command,
1235
+ [
1236
+ "--goo",
1237
+ "gar",
1238
+ "--foo",
1239
+ "bar",
1240
+ ],
1241
+ undefined,
1242
+ );
1243
+ expectExtractResult(result, { foo: "bar" }, ["--goo", "gar"]);
1244
+ expect(result.invalidArgument).toBeUndefined();
1245
+
1246
+ result = populateSubCommandValues(
1247
+ command,
1248
+ ["--goo=gar", "--foo=bar"],
1249
+ undefined,
1250
+ );
1251
+ expectExtractResult(result, { foo: "bar" }, ["--goo=gar"]);
1252
+ expect(result.invalidArgument).toBeUndefined();
1253
+
1254
+ command = {
1255
+ name: "command",
1256
+ options: [{
1257
+ name: "foo",
1258
+ shortAlias: "f",
1259
+ type: ArgumentValueTypeName.BOOLEAN,
1260
+ }],
1261
+ positionals: [],
1262
+ execute: async (): Promise<void> => {
1263
+ },
1264
+ };
1265
+
1266
+ result = populateSubCommandValues(
1267
+ command,
1268
+ ["--goo", "gar", "-f"],
1269
+ undefined,
1270
+ );
1271
+ expectExtractResult(result, { foo: "true" }, ["--goo", "gar"]);
1272
+ expect(result.invalidArgument).toBeUndefined();
1273
+ });
1274
+
1275
+ test("Unused option after positional", () => {
1276
+ const command: SubCommand = {
1277
+ name: "command",
1278
+ options: [{
1279
+ name: "foo1",
1280
+ shortAlias: "1",
1281
+ type: ArgumentValueTypeName.STRING,
1282
+ }],
1283
+ positionals: [{
1284
+ name: "foo2",
1285
+ type: ArgumentValueTypeName.STRING,
1286
+ }],
1287
+ execute: async (): Promise<void> => {
1288
+ },
1289
+ };
1290
+
1291
+ const result = populateSubCommandValues(
1292
+ command,
1293
+ [
1294
+ "--foo1",
1295
+ "bar",
1296
+ "foo2",
1297
+ "--goo",
1298
+ "gar",
1299
+ ],
1300
+ undefined,
1301
+ );
1302
+ expectExtractResult(result, { foo1: "bar", foo2: "foo2" }, [
1303
+ "--goo",
1304
+ "gar",
1305
+ ]);
1306
+ expect(result.invalidArgument).toBeUndefined();
1307
+ });
1308
+
1309
+ test("Unused option before positional", () => {
1310
+ const command: SubCommand = {
1311
+ name: "command",
1312
+ options: [{
1313
+ name: "foo1",
1314
+ shortAlias: "1",
1315
+ type: ArgumentValueTypeName.STRING,
1316
+ }],
1317
+ positionals: [{
1318
+ name: "foo2",
1319
+ type: ArgumentValueTypeName.STRING,
1320
+ }],
1321
+ execute: async (): Promise<void> => {
1322
+ },
1323
+ };
1324
+
1325
+ const result = populateSubCommandValues(
1326
+ command,
1327
+ [
1328
+ "--goo", // as this is unrecognised as an option for the command it will be used as a positional value
1329
+ "gar",
1330
+ "foo2",
1331
+ "--foo1",
1332
+ "bar",
1333
+ ],
1334
+ undefined,
1335
+ );
1336
+ expectExtractResult(result, { foo1: "bar", foo2: "--goo" }, [
1337
+ "gar",
1338
+ "foo2",
1339
+ ]);
1340
+ expect(result.invalidArgument).toBeUndefined();
1341
+ });
1342
+
1343
+ test("Unused positional after positional", () => {
1344
+ const command: SubCommand = {
1345
+ name: "command",
1346
+ options: [],
1347
+ positionals: [{
1348
+ name: "foo",
1349
+ type: ArgumentValueTypeName.STRING,
1350
+ }],
1351
+ execute: async (): Promise<void> => {
1352
+ },
1353
+ };
1354
+
1355
+ const result = populateSubCommandValues(
1356
+ command,
1357
+ ["bar", "goo"],
1358
+ undefined,
1359
+ );
1360
+ expectExtractResult(result, { foo: "bar" }, ["goo"]);
1361
+ expect(result.invalidArgument).toBeUndefined();
1362
+ });
1363
+
1364
+ test("Unused positional after optional", () => {
1365
+ let command: SubCommand = {
1366
+ name: "command",
1367
+ options: [{
1368
+ name: "foo",
1369
+ type: ArgumentValueTypeName.STRING,
1370
+ }],
1371
+ positionals: [],
1372
+ execute: async (): Promise<void> => {
1373
+ },
1374
+ };
1375
+
1376
+ let result = populateSubCommandValues(
1377
+ command,
1378
+ ["--foo", "bar", "goo"],
1379
+ undefined,
1380
+ );
1381
+ expectExtractResult(result, { foo: "bar" }, ["goo"]);
1382
+ expect(result.invalidArgument).toBeUndefined();
1383
+
1384
+ command = {
1385
+ name: "command",
1386
+ options: [{
1387
+ name: "foo",
1388
+ type: ArgumentValueTypeName.BOOLEAN,
1389
+ }],
1390
+ positionals: [],
1391
+ execute: async (): Promise<void> => {
1392
+ },
1393
+ };
1394
+
1395
+ result = populateSubCommandValues(
1396
+ command,
1397
+ ["--foo", "goo"],
1398
+ undefined,
1399
+ );
1400
+ expectExtractResult(result, { foo: "true" }, ["goo"]);
1401
+ expect(result.invalidArgument).toBeUndefined();
1402
+ });
1403
+
1404
+ test("Positionals either side of options", () => {
1405
+ let command: SubCommand = {
1406
+ name: "command",
1407
+ options: [{
1408
+ name: "goo1",
1409
+ type: ArgumentValueTypeName.STRING,
1410
+ }, {
1411
+ name: "goo2",
1412
+ type: ArgumentValueTypeName.STRING,
1413
+ }],
1414
+ positionals: [{
1415
+ name: "foo1",
1416
+ type: ArgumentValueTypeName.STRING,
1417
+ }, {
1418
+ name: "foo2",
1419
+ type: ArgumentValueTypeName.STRING,
1420
+ }],
1421
+ execute: async (): Promise<void> => {
1422
+ },
1423
+ };
1424
+
1425
+ let result = populateSubCommandValues(
1426
+ command,
1427
+ [
1428
+ "f1",
1429
+ "--goo1",
1430
+ "g1",
1431
+ "--goo2",
1432
+ "g2",
1433
+ "f2",
1434
+ ],
1435
+ undefined,
1436
+ );
1437
+ expectExtractResult(result, {
1438
+ goo1: "g1",
1439
+ goo2: "g2",
1440
+ foo1: "f1",
1441
+ foo2: "f2",
1442
+ }, []);
1443
+ expect(result.invalidArgument).toBeUndefined();
1444
+
1445
+ command = {
1446
+ name: "command",
1447
+ options: [{
1448
+ name: "goo1",
1449
+ type: ArgumentValueTypeName.BOOLEAN,
1450
+ }, {
1451
+ name: "goo2",
1452
+ type: ArgumentValueTypeName.STRING,
1453
+ }],
1454
+ positionals: [{
1455
+ name: "foo1",
1456
+ type: ArgumentValueTypeName.BOOLEAN,
1457
+ }, {
1458
+ name: "foo2",
1459
+ type: ArgumentValueTypeName.STRING,
1460
+ }],
1461
+ execute: async (): Promise<void> => {
1462
+ },
1463
+ };
1464
+
1465
+ result = populateSubCommandValues(
1466
+ command,
1467
+ [
1468
+ "true",
1469
+ "--goo1",
1470
+ "--goo2",
1471
+ "g2",
1472
+ "f2",
1473
+ ],
1474
+ undefined,
1475
+ );
1476
+ expectExtractResult(result, {
1477
+ goo1: "true",
1478
+ goo2: "g2",
1479
+ foo1: "true",
1480
+ foo2: "f2",
1481
+ }, []);
1482
+ expect(result.invalidArgument).toBeUndefined();
1483
+ });
1484
+
1485
+ test("Positionals and non-boolean options interleaved", () => {
1486
+ const command: SubCommand = {
1487
+ name: "command",
1488
+ options: [{
1489
+ name: "goo1",
1490
+ type: ArgumentValueTypeName.STRING,
1491
+ }, {
1492
+ name: "goo2",
1493
+ type: ArgumentValueTypeName.STRING,
1494
+ }],
1495
+ positionals: [{
1496
+ name: "foo1",
1497
+ type: ArgumentValueTypeName.STRING,
1498
+ }, {
1499
+ name: "foo2",
1500
+ type: ArgumentValueTypeName.STRING,
1501
+ }],
1502
+ execute: async (): Promise<void> => {
1503
+ },
1504
+ };
1505
+
1506
+ let result = populateSubCommandValues(
1507
+ command,
1508
+ [
1509
+ "f1",
1510
+ "--goo1",
1511
+ "g1",
1512
+ "f2",
1513
+ "--goo2",
1514
+ "g2",
1515
+ ],
1516
+ undefined,
1517
+ );
1518
+ expectExtractResult(result, {
1519
+ goo1: "g1",
1520
+ goo2: "g2",
1521
+ foo1: "f1",
1522
+ foo2: "f2",
1523
+ }, []);
1524
+ expect(result.invalidArgument).toBeUndefined();
1525
+
1526
+ result = populateSubCommandValues(
1527
+ command,
1528
+ [
1529
+ "--goo1",
1530
+ "g1",
1531
+ "f1",
1532
+ "--goo2",
1533
+ "g2",
1534
+ "f2",
1535
+ ],
1536
+ undefined,
1537
+ );
1538
+ expectExtractResult(result, {
1539
+ goo1: "g1",
1540
+ goo2: "g2",
1541
+ foo1: "f1",
1542
+ foo2: "f2",
1543
+ }, []);
1544
+ expect(result.invalidArgument).toBeUndefined();
1545
+ });
1546
+
1547
+ test("Positionals and boolean options interleaved", () => {
1548
+ const command: SubCommand = {
1549
+ name: "command",
1550
+ options: [{
1551
+ name: "goo1",
1552
+ type: ArgumentValueTypeName.BOOLEAN,
1553
+ }, {
1554
+ name: "goo2",
1555
+ type: ArgumentValueTypeName.BOOLEAN,
1556
+ }],
1557
+ positionals: [{
1558
+ name: "foo1",
1559
+ type: ArgumentValueTypeName.STRING,
1560
+ }, {
1561
+ name: "foo2",
1562
+ type: ArgumentValueTypeName.STRING,
1563
+ }],
1564
+ execute: async (): Promise<void> => {
1565
+ },
1566
+ };
1567
+
1568
+ let result = populateSubCommandValues(
1569
+ command,
1570
+ [
1571
+ "f1",
1572
+ "--goo1",
1573
+ "true",
1574
+ "f2",
1575
+ "--goo2",
1576
+ "true",
1577
+ ],
1578
+ undefined,
1579
+ );
1580
+ expectExtractResult(result, {
1581
+ goo1: "true",
1582
+ goo2: "true",
1583
+ foo1: "f1",
1584
+ foo2: "f2",
1585
+ }, []);
1586
+ expect(result.invalidArgument).toBeUndefined();
1587
+
1588
+ result = populateSubCommandValues(
1589
+ command,
1590
+ ["f1", "--goo1", "f2", "--goo2"],
1591
+ undefined,
1592
+ );
1593
+ expectExtractResult(result, {
1594
+ goo1: "true",
1595
+ goo2: "true",
1596
+ foo1: "f1",
1597
+ foo2: "f2",
1598
+ }, []);
1599
+ expect(result.invalidArgument).toBeUndefined();
1600
+ });
1601
+
1602
+ test("Array option before vararg positional", () => {
1603
+ const command: SubCommand = {
1604
+ name: "command",
1605
+ options: [{
1606
+ name: "foo",
1607
+ type: ArgumentValueTypeName.STRING,
1608
+ isArray: true,
1609
+ }],
1610
+ positionals: [{
1611
+ name: "goo",
1612
+ type: ArgumentValueTypeName.STRING,
1613
+ isVarargMultiple: true,
1614
+ }],
1615
+ execute: async (): Promise<void> => {
1616
+ },
1617
+ };
1618
+
1619
+ const result = populateSubCommandValues(
1620
+ command,
1621
+ [
1622
+ "--foo",
1623
+ "f1",
1624
+ "--foo",
1625
+ "f2",
1626
+ "g1",
1627
+ "g2",
1628
+ ],
1629
+ undefined,
1630
+ );
1631
+ expectExtractResult(result, {
1632
+ foo: ["f1", "f2"],
1633
+ goo: ["g1", "g2"],
1634
+ }, []);
1635
+ expect(result.invalidArgument).toBeUndefined();
1636
+ });
1637
+
1638
+ test("Vararg positional before array option", () => {
1639
+ const command: SubCommand = {
1640
+ name: "command",
1641
+ options: [{
1642
+ name: "foo",
1643
+ type: ArgumentValueTypeName.STRING,
1644
+ isArray: true,
1645
+ }],
1646
+ positionals: [{
1647
+ name: "goo",
1648
+ type: ArgumentValueTypeName.STRING,
1649
+ isVarargMultiple: true,
1650
+ isVarargOptional: true,
1651
+ }],
1652
+ execute: async (): Promise<void> => {
1653
+ },
1654
+ };
1655
+
1656
+ let result = populateSubCommandValues(
1657
+ command,
1658
+ [
1659
+ "g1",
1660
+ "g2",
1661
+ "--foo",
1662
+ "f1",
1663
+ "--foo",
1664
+ "f2",
1665
+ ],
1666
+ undefined,
1667
+ );
1668
+ expectExtractResult(result, {
1669
+ foo: ["f1", "f2"],
1670
+ goo: ["g1", "g2"],
1671
+ }, []);
1672
+ expect(result.invalidArgument).toBeUndefined();
1673
+
1674
+ result = populateSubCommandValues(
1675
+ command,
1676
+ ["--foo", "f1", "--foo", "f2"],
1677
+ undefined,
1678
+ );
1679
+ expectExtractResult(result, {
1680
+ foo: ["f1", "f2"],
1681
+ }, []);
1682
+ expect(result.invalidArgument).toBeUndefined();
1683
+ });
1684
+
1685
+ test("Positional types configured value is overridden by arg", () => {
1686
+ let command: SubCommand = {
1687
+ name: "command",
1688
+ options: [],
1689
+ positionals: [{
1690
+ name: "foo",
1691
+ type: ArgumentValueTypeName.NUMBER,
1692
+ }],
1693
+ execute: async (): Promise<void> => {
1694
+ },
1695
+ };
1696
+
1697
+ let result = populateSubCommandValues(
1698
+ command,
1699
+ ["1"],
1700
+ { foo: 2 },
1701
+ );
1702
+ expectExtractResult(result, { foo: "1" }, []);
1703
+ expect(result.invalidArgument).toBeUndefined();
1704
+
1705
+ command = {
1706
+ name: "command",
1707
+ options: [],
1708
+ positionals: [{
1709
+ name: "foo",
1710
+ type: ArgumentValueTypeName.STRING,
1711
+ }],
1712
+ execute: async (): Promise<void> => {
1713
+ },
1714
+ };
1715
+ result = populateSubCommandValues(
1716
+ command,
1717
+ ["bar"],
1718
+ { foo: "nope" },
1719
+ );
1720
+ expectExtractResult(result, { foo: "bar" }, []);
1721
+ expect(result.invalidArgument).toBeUndefined();
1722
+
1723
+ command = {
1724
+ name: "command",
1725
+ options: [],
1726
+ positionals: [{
1727
+ name: "foo",
1728
+ type: ArgumentValueTypeName.BOOLEAN,
1729
+ }],
1730
+ execute: async (): Promise<void> => {
1731
+ },
1732
+ };
1733
+ result = populateSubCommandValues(
1734
+ command,
1735
+ ["true"],
1736
+ { foo: false },
1737
+ );
1738
+ expectExtractResult(result, { foo: "true" }, []);
1739
+ expect(result.invalidArgument).toBeUndefined();
1740
+ });
1741
+
1742
+ test("Positional types configured value is used for optional vararg with no arg provided", () => {
1743
+ let command: SubCommand = {
1744
+ name: "command",
1745
+ options: [],
1746
+ positionals: [{
1747
+ name: "foo",
1748
+ type: ArgumentValueTypeName.NUMBER,
1749
+ isVarargOptional: true,
1750
+ }],
1751
+ execute: async (): Promise<void> => {
1752
+ },
1753
+ };
1754
+
1755
+ let result = populateSubCommandValues(
1756
+ command,
1757
+ [],
1758
+ { foo: 1 },
1759
+ );
1760
+ expectExtractResult(result, { foo: 1 }, []);
1761
+ expect(result.invalidArgument).toBeUndefined();
1762
+
1763
+ command = {
1764
+ name: "command",
1765
+ options: [],
1766
+ positionals: [{
1767
+ name: "foo",
1768
+ type: ArgumentValueTypeName.STRING,
1769
+ isVarargOptional: true,
1770
+ }],
1771
+ execute: async (): Promise<void> => {
1772
+ },
1773
+ };
1774
+ result = populateSubCommandValues(
1775
+ command,
1776
+ [],
1777
+ { foo: "bar" },
1778
+ );
1779
+ expectExtractResult(result, { foo: "bar" }, []);
1780
+ expect(result.invalidArgument).toBeUndefined();
1781
+
1782
+ command = {
1783
+ name: "command",
1784
+ options: [],
1785
+ positionals: [{
1786
+ name: "foo",
1787
+ type: ArgumentValueTypeName.BOOLEAN,
1788
+ isVarargOptional: true,
1789
+ }],
1790
+ execute: async (): Promise<void> => {
1791
+ },
1792
+ };
1793
+ result = populateSubCommandValues(
1794
+ command,
1795
+ [],
1796
+ { foo: true },
1797
+ );
1798
+ expectExtractResult(result, { foo: true }, []);
1799
+ expect(result.invalidArgument).toBeUndefined();
1800
+ });
1801
+
1802
+ test("Positional types configured value is used for mandatory vararg with no arg provided", () => {
1803
+ let command: SubCommand = {
1804
+ name: "command",
1805
+ options: [],
1806
+ positionals: [{
1807
+ name: "foo",
1808
+ type: ArgumentValueTypeName.NUMBER,
1809
+ isVarargOptional: false,
1810
+ isVarargMultiple: true,
1811
+ }],
1812
+ execute: async (): Promise<void> => {
1813
+ },
1814
+ };
1815
+
1816
+ let result = populateSubCommandValues(
1817
+ command,
1818
+ [],
1819
+ { foo: 1 },
1820
+ );
1821
+ expectExtractResult(result, { foo: 1 }, []);
1822
+ expect(result.invalidArgument).toBeUndefined();
1823
+
1824
+ command = {
1825
+ name: "command",
1826
+ options: [],
1827
+ positionals: [{
1828
+ name: "foo",
1829
+ type: ArgumentValueTypeName.STRING,
1830
+ isVarargOptional: false,
1831
+ isVarargMultiple: true,
1832
+ }],
1833
+ execute: async (): Promise<void> => {
1834
+ },
1835
+ };
1836
+ result = populateSubCommandValues(
1837
+ command,
1838
+ [],
1839
+ { foo: "bar" },
1840
+ );
1841
+ expectExtractResult(result, { foo: "bar" }, []);
1842
+ expect(result.invalidArgument).toBeUndefined();
1843
+
1844
+ command = {
1845
+ name: "command",
1846
+ options: [],
1847
+ positionals: [{
1848
+ name: "foo",
1849
+ type: ArgumentValueTypeName.BOOLEAN,
1850
+ isVarargOptional: false,
1851
+ isVarargMultiple: true,
1852
+ }],
1853
+ execute: async (): Promise<void> => {
1854
+ },
1855
+ };
1856
+ result = populateSubCommandValues(
1857
+ command,
1858
+ [],
1859
+ { foo: true },
1860
+ );
1861
+ expectExtractResult(result, { foo: true }, []);
1862
+ expect(result.invalidArgument).toBeUndefined();
1863
+ });
1864
+
1865
+ test("Option array configured single values completely overridden by multiple args", () => {
1866
+ const command: SubCommand = {
1867
+ name: "command",
1868
+ options: [{
1869
+ name: "foo",
1870
+ type: ArgumentValueTypeName.STRING,
1871
+ isArray: true,
1872
+ }],
1873
+ positionals: [],
1874
+ execute: async (): Promise<void> => {
1875
+ },
1876
+ };
1877
+
1878
+ const result = populateSubCommandValues(
1879
+ command,
1880
+ ["--foo", "bar1", "--foo", "bar2"],
1881
+ { foo: ["bar"] },
1882
+ );
1883
+ expectExtractResult(result, { foo: ["bar1", "bar2"] }, []);
1884
+ expect(result.invalidArgument).toBeUndefined();
1885
+ });
1886
+
1887
+ test("Option array configured multiple values partially overridden by arg", () => {
1888
+ const command: SubCommand = {
1889
+ name: "command",
1890
+ options: [{
1891
+ name: "foo",
1892
+ type: ArgumentValueTypeName.STRING,
1893
+ isArray: true,
1894
+ }],
1895
+ positionals: [],
1896
+ execute: async (): Promise<void> => {
1897
+ },
1898
+ };
1899
+
1900
+ const result = populateSubCommandValues(
1901
+ command,
1902
+ ["--foo", "bar"],
1903
+ { foo: ["bar1", "bar2"] },
1904
+ );
1905
+ expectExtractResult(result, { foo: ["bar", "bar2"] }, []);
1906
+ expect(result.invalidArgument).toBeUndefined();
1907
+ });
1908
+
1909
+ test("Positional argument configured single value completely overridden by multiple args", () => {
1910
+ const command: SubCommand = {
1911
+ name: "command",
1912
+ options: [],
1913
+ positionals: [{
1914
+ name: "foo",
1915
+ type: ArgumentValueTypeName.STRING,
1916
+ isVarargMultiple: true,
1917
+ }],
1918
+ execute: async (): Promise<void> => {
1919
+ },
1920
+ };
1921
+
1922
+ const result = populateSubCommandValues(command, ["bar", "bar"], {
1923
+ foo: "bar1",
1924
+ });
1925
+ expectExtractResult(result, { foo: ["bar", "bar"] }, []);
1926
+ expect(result.invalidArgument).toBeUndefined();
1927
+ });
1928
+
1929
+ test("Positional argument configured multiple values partially overridden by single arg", () => {
1930
+ const command: SubCommand = {
1931
+ name: "command",
1932
+ options: [],
1933
+ positionals: [{
1934
+ name: "foo",
1935
+ type: ArgumentValueTypeName.STRING,
1936
+ isVarargMultiple: true,
1937
+ }],
1938
+ execute: async (): Promise<void> => {
1939
+ },
1940
+ };
1941
+
1942
+ const result = populateSubCommandValues(command, ["bar"], {
1943
+ foo: ["bar1", "bar2"],
1944
+ });
1945
+ expectExtractResult(result, { foo: ["bar", "bar2"] }, []);
1946
+ expect(result.invalidArgument).toBeUndefined();
1947
+ });
1948
+
1949
+ test("Option array using indices", () => {
1950
+ const command: SubCommand = {
1951
+ name: "command",
1952
+ options: [{
1953
+ name: "foo",
1954
+ type: ArgumentValueTypeName.STRING,
1955
+ shortAlias: "f",
1956
+ isArray: true,
1957
+ }],
1958
+ positionals: [],
1959
+ execute: async (): Promise<void> => {
1960
+ },
1961
+ };
1962
+
1963
+ let result = populateSubCommandValues(
1964
+ command,
1965
+ ["--foo[0]", "bar"],
1966
+ undefined,
1967
+ );
1968
+ expectExtractResult(result, { foo: ["bar"] }, []);
1969
+ expect(result.invalidArgument).toBeUndefined();
1970
+
1971
+ result = populateSubCommandValues(
1972
+ command,
1973
+ ["--foo[1]", "bar1", "--foo[0]", "bar0"],
1974
+ undefined,
1975
+ );
1976
+ expectExtractResult(result, { foo: ["bar0", "bar1"] }, []);
1977
+ expect(result.invalidArgument).toBeUndefined();
1978
+
1979
+ result = populateSubCommandValues(
1980
+ command,
1981
+ ["--foo[0]=bar0", "--foo[1]", "bar1"],
1982
+ undefined,
1983
+ );
1984
+ expectExtractResult(result, { foo: ["bar0", "bar1"] }, []);
1985
+ expect(result.invalidArgument).toBeUndefined();
1986
+
1987
+ result = populateSubCommandValues(
1988
+ command,
1989
+ ["--foo[1]=bar1", "-f[0]", "bar0"],
1990
+ undefined,
1991
+ );
1992
+ expectExtractResult(result, { foo: ["bar0", "bar1"] }, []);
1993
+ expect(result.invalidArgument).toBeUndefined();
1994
+ });
1995
+
1996
+ test("Option array invalid indices", () => {
1997
+ const command: SubCommand = {
1998
+ name: "command",
1999
+ options: [{
2000
+ name: "foo",
2001
+ type: ArgumentValueTypeName.STRING,
2002
+ shortAlias: "f",
2003
+ isArray: true,
2004
+ }],
2005
+ positionals: [],
2006
+ execute: async (): Promise<void> => {
2007
+ },
2008
+ };
2009
+
2010
+ let result = populateSubCommandValues(
2011
+ command,
2012
+ ["--foo[1]", "bar"],
2013
+ undefined,
2014
+ );
2015
+ let expected = {
2016
+ foo: [] as Array<string>,
2017
+ };
2018
+ expected.foo[1] = "bar";
2019
+ expectExtractResult(result, expected, []);
2020
+ expect(result.invalidArgument).toBeUndefined();
2021
+
2022
+ result = populateSubCommandValues(
2023
+ command,
2024
+ ["--foo[2]", "bar2", "--foo[0]", "bar0"],
2025
+ undefined,
2026
+ );
2027
+ expected = {
2028
+ foo: [] as Array<string>,
2029
+ };
2030
+ expected.foo[0] = "bar0";
2031
+ expected.foo[2] = "bar2";
2032
+ expectExtractResult(result, expected, []);
2033
+ expect(result.invalidArgument).toBeUndefined();
2034
+ });
2035
+
2036
+ test("Option array missing indices populated by configured defaults", () => {
2037
+ const command: SubCommand = {
2038
+ name: "command",
2039
+ options: [{
2040
+ name: "foo",
2041
+ type: ArgumentValueTypeName.STRING,
2042
+ shortAlias: "f",
2043
+ isArray: true,
2044
+ }],
2045
+ positionals: [],
2046
+ execute: async (): Promise<void> => {
2047
+ },
2048
+ };
2049
+
2050
+ let result = populateSubCommandValues(
2051
+ command,
2052
+ ["--foo[1]", "bar"],
2053
+ { foo: ["car"] },
2054
+ );
2055
+ expectExtractResult(result, { foo: ["car", "bar"] }, []);
2056
+ expect(result.invalidArgument).toBeUndefined();
2057
+
2058
+ result = populateSubCommandValues(
2059
+ command,
2060
+ ["--foo[2]", "bar1", "--foo[0]", "bar0"],
2061
+ { foo: ["bar", "car"] },
2062
+ );
2063
+ expectExtractResult(result, { foo: ["bar0", "car", "bar1"] }, []);
2064
+ expect(result.invalidArgument).toBeUndefined();
2065
+ });
2066
+
2067
+ test("Option array using indices either side of positional", () => {
2068
+ let command: SubCommand = {
2069
+ name: "command",
2070
+ options: [{
2071
+ name: "foo",
2072
+ isArray: true,
2073
+ type: ArgumentValueTypeName.STRING,
2074
+ }],
2075
+ positionals: [{
2076
+ name: "goo",
2077
+ type: ArgumentValueTypeName.STRING,
2078
+ }],
2079
+ execute: async (): Promise<void> => {
2080
+ },
2081
+ };
2082
+
2083
+ let result = populateSubCommandValues(
2084
+ command,
2085
+ [
2086
+ "--foo[0]",
2087
+ "f1",
2088
+ "goo1",
2089
+ "--foo[1]",
2090
+ "f2",
2091
+ ],
2092
+ undefined,
2093
+ );
2094
+ expectExtractResult(result, {
2095
+ foo: ["f1", "f2"],
2096
+ goo: "goo1",
2097
+ }, []);
2098
+ expect(result.invalidArgument).toBeUndefined();
2099
+
2100
+ command = {
2101
+ name: "command",
2102
+ options: [{
2103
+ name: "foo",
2104
+ isArray: true,
2105
+ type: ArgumentValueTypeName.BOOLEAN,
2106
+ }],
2107
+ positionals: [{
2108
+ name: "goo",
2109
+ type: ArgumentValueTypeName.STRING,
2110
+ }],
2111
+ execute: async (): Promise<void> => {
2112
+ },
2113
+ };
2114
+ result = populateSubCommandValues(
2115
+ command,
2116
+ [
2117
+ "--foo[1]",
2118
+ "true",
2119
+ "goo1",
2120
+ "--foo[0]",
2121
+ "false",
2122
+ ],
2123
+ undefined,
2124
+ );
2125
+ expectExtractResult(result, {
2126
+ foo: ["false", "true"],
2127
+ goo: "goo1",
2128
+ }, []);
2129
+ expect(result.invalidArgument).toBeUndefined();
2130
+
2131
+ result = populateSubCommandValues(
2132
+ command,
2133
+ ["--foo[1]", "goo1", "--foo[0]"],
2134
+ undefined,
2135
+ );
2136
+ expectExtractResult(result, {
2137
+ foo: ["true", "true"],
2138
+ goo: "goo1",
2139
+ }, []);
2140
+ expect(result.invalidArgument).toBeUndefined();
2141
+ });
2142
+
2143
+ test("Option complex values", () => {
2144
+ const command: SubCommand = {
2145
+ name: "command",
2146
+ options: [{
2147
+ name: "alpha",
2148
+ type: ComplexValueTypeName.COMPLEX,
2149
+ shortAlias: "a",
2150
+ properties: [{
2151
+ name: "beta",
2152
+ type: ComplexValueTypeName.COMPLEX,
2153
+ shortAlias: "b",
2154
+ properties: [{
2155
+ name: "gamma",
2156
+ type: ArgumentValueTypeName.STRING,
2157
+ shortAlias: "g",
2158
+ }, {
2159
+ name: "delta",
2160
+ type: ArgumentValueTypeName.STRING,
2161
+ shortAlias: "d",
2162
+ }],
2163
+ }],
2164
+ }],
2165
+ positionals: [],
2166
+ execute: async (): Promise<void> => {
2167
+ },
2168
+ };
2169
+
2170
+ let result = populateSubCommandValues(
2171
+ command,
2172
+ ["--alpha.beta.gamma", "foo", "--alpha.beta.delta", "bar"],
2173
+ undefined,
2174
+ );
2175
+ expectExtractResult(result, {
2176
+ alpha: { beta: { gamma: "foo", delta: "bar" } },
2177
+ }, []);
2178
+ expect(result.invalidArgument).toBeUndefined();
2179
+
2180
+ result = populateSubCommandValues(
2181
+ command,
2182
+ ["-a.b.g", "foo", "-a.b.d", "bar"],
2183
+ undefined,
2184
+ );
2185
+ expectExtractResult(result, {
2186
+ alpha: { beta: { gamma: "foo", delta: "bar" } },
2187
+ }, []);
2188
+ expect(result.invalidArgument).toBeUndefined();
2189
+ });
2190
+
2191
+ test("Option arrays of complex values and vice versa", () => {
2192
+ const command: SubCommand = {
2193
+ name: "command",
2194
+ options: [{
2195
+ name: "alpha",
2196
+ type: ComplexValueTypeName.COMPLEX,
2197
+ shortAlias: "a",
2198
+ isArray: true,
2199
+ properties: [{
2200
+ name: "beta",
2201
+ type: ComplexValueTypeName.COMPLEX,
2202
+ shortAlias: "b",
2203
+ properties: [{
2204
+ name: "gamma",
2205
+ type: ArgumentValueTypeName.STRING,
2206
+ shortAlias: "g",
2207
+ }, {
2208
+ name: "delta",
2209
+ type: ArgumentValueTypeName.STRING,
2210
+ shortAlias: "d",
2211
+ isArray: true,
2212
+ }],
2213
+ }],
2214
+ }],
2215
+ positionals: [],
2216
+ execute: async (): Promise<void> => {
2217
+ },
2218
+ };
2219
+
2220
+ let result = populateSubCommandValues(
2221
+ command,
2222
+ ["--alpha.beta.gamma", "foo", "--alpha.beta.delta", "bar"],
2223
+ undefined,
2224
+ );
2225
+ expectExtractResult(result, {
2226
+ alpha: [{ beta: { gamma: "foo", delta: ["bar"] } }],
2227
+ }, []);
2228
+ expect(result.invalidArgument).toBeUndefined();
2229
+
2230
+ result = populateSubCommandValues(
2231
+ command,
2232
+ ["-a[0].b.d[0]", "bar"],
2233
+ undefined,
2234
+ );
2235
+ expectExtractResult(result, {
2236
+ alpha: [{ beta: { delta: ["bar"] } }],
2237
+ }, []);
2238
+ expect(result.invalidArgument).toBeUndefined();
2239
+
2240
+ result = populateSubCommandValues(
2241
+ command,
2242
+ ["-a[0].b.g", "foo", "-a[0].b.d[0]", "bar"],
2243
+ undefined,
2244
+ );
2245
+ expectExtractResult(result, {
2246
+ alpha: [{ beta: { gamma: "foo", delta: ["bar"] } }],
2247
+ }, []);
2248
+ expect(result.invalidArgument).toBeUndefined();
2249
+
2250
+ result = populateSubCommandValues(
2251
+ command,
2252
+ [
2253
+ "-a[0].b.g",
2254
+ "foo",
2255
+ "-a[0].b.d[0]",
2256
+ "bar",
2257
+ "-a[0].b.d[1]",
2258
+ "car",
2259
+ "-a[1].b.g",
2260
+ "foo",
2261
+ "-a[1].b.d[1]",
2262
+ "bar",
2263
+ "-a[1].b.d[0]",
2264
+ "car",
2265
+ ],
2266
+ undefined,
2267
+ );
2268
+ expectExtractResult(result, {
2269
+ alpha: [{ beta: { gamma: "foo", delta: ["bar", "car"] } }, {
2270
+ beta: { gamma: "foo", delta: ["car", "bar"] },
2271
+ }],
2272
+ }, []);
2273
+ expect(result.invalidArgument).toBeUndefined();
2274
+ });
2275
+
2276
+ test("Option complex values with invalid properties", () => {
2277
+ const command: SubCommand = {
2278
+ name: "command",
2279
+ options: [{
2280
+ name: "alpha",
2281
+ type: ComplexValueTypeName.COMPLEX,
2282
+ shortAlias: "a",
2283
+ properties: [{
2284
+ name: "beta",
2285
+ type: ComplexValueTypeName.COMPLEX,
2286
+ shortAlias: "b",
2287
+ properties: [{
2288
+ name: "gamma",
2289
+ type: ArgumentValueTypeName.STRING,
2290
+ shortAlias: "g",
2291
+ }, {
2292
+ name: "delta",
2293
+ type: ArgumentValueTypeName.STRING,
2294
+ shortAlias: "d",
2295
+ }],
2296
+ }],
2297
+ }],
2298
+ positionals: [],
2299
+ execute: async (): Promise<void> => {
2300
+ },
2301
+ };
2302
+
2303
+ let result = populateSubCommandValues(
2304
+ command,
2305
+ ["--alpha.beta.delta", "bar", "--alpha.beta.gamma"],
2306
+ undefined,
2307
+ );
2308
+ expectExtractResult(result, {
2309
+ alpha: { beta: { delta: "bar" } },
2310
+ }, []);
2311
+ expect(result.invalidArgument).toEqual({
2312
+ argument:
2313
+ ((command.options[0] as ComplexOption).properties[0] as ComplexOption)
2314
+ .properties[0],
2315
+ name: "--alpha.beta.gamma",
2316
+ reason: InvalidArgumentReason.MISSING_VALUE,
2317
+ });
2318
+
2319
+ result = populateSubCommandValues(
2320
+ command,
2321
+ ["-a.b.g", "foo", "--alpha.beta.delta"],
2322
+ undefined,
2323
+ );
2324
+ expectExtractResult(result, {
2325
+ alpha: { beta: { gamma: "foo" } },
2326
+ }, []);
2327
+ expect(result.invalidArgument).toEqual({
2328
+ argument:
2329
+ ((command.options[0] as ComplexOption).properties[0] as ComplexOption)
2330
+ .properties[1],
2331
+ name: "--alpha.beta.delta",
2332
+ reason: InvalidArgumentReason.MISSING_VALUE,
2333
+ });
2334
+
2335
+ result = populateSubCommandValues(
2336
+ command,
2337
+ ["-a.b.x", "foo"],
2338
+ undefined,
2339
+ );
2340
+ expectExtractResult(result, {
2341
+ alpha: { beta: {} },
2342
+ }, ["foo"]);
2343
+ expect(result.invalidArgument).toEqual({
2344
+ name: "-a.b.x",
2345
+ reason: InvalidArgumentReason.UNKNOWN_PROPERTY,
2346
+ });
2347
+
2348
+ result = populateSubCommandValues(
2349
+ command,
2350
+ ["-a.b.g.a", "foo"],
2351
+ undefined,
2352
+ );
2353
+ expectExtractResult(result, {
2354
+ alpha: { beta: {} },
2355
+ }, ["foo"]);
2356
+ expect(result.invalidArgument).toEqual({
2357
+ name: "-a.b.g.a",
2358
+ reason: InvalidArgumentReason.UNKNOWN_PROPERTY,
2359
+ });
2360
+ });
2361
+
2362
+ test("Option complex values with missing properties supplied by configured defaults", () => {
2363
+ const command: SubCommand = {
2364
+ name: "command",
2365
+ options: [{
2366
+ name: "alpha",
2367
+ type: ComplexValueTypeName.COMPLEX,
2368
+ shortAlias: "a",
2369
+ properties: [{
2370
+ name: "beta",
2371
+ type: ComplexValueTypeName.COMPLEX,
2372
+ shortAlias: "b",
2373
+ properties: [{
2374
+ name: "gamma",
2375
+ type: ArgumentValueTypeName.STRING,
2376
+ shortAlias: "g",
2377
+ }, {
2378
+ name: "delta",
2379
+ type: ArgumentValueTypeName.STRING,
2380
+ shortAlias: "d",
2381
+ }],
2382
+ }],
2383
+ }],
2384
+ positionals: [],
2385
+ execute: async (): Promise<void> => {
2386
+ },
2387
+ };
2388
+
2389
+ let result = populateSubCommandValues(
2390
+ command,
2391
+ ["--alpha.beta.delta", "bar"],
2392
+ { alpha: { beta: { gamma: "foo" } } },
2393
+ );
2394
+ expectExtractResult(result, {
2395
+ alpha: { beta: { gamma: "foo", delta: "bar" } },
2396
+ }, []);
2397
+ expect(result.invalidArgument).toBeUndefined();
2398
+
2399
+ result = populateSubCommandValues(
2400
+ command,
2401
+ ["-a.b.g", "foo"],
2402
+ { alpha: { beta: { delta: "bar" } } },
2403
+ );
2404
+ expectExtractResult(result, {
2405
+ alpha: { beta: { gamma: "foo", delta: "bar" } },
2406
+ }, []);
2407
+ expect(result.invalidArgument).toBeUndefined();
2408
+ });
2409
+
2410
+ test("Option array check for maximum array size", () => {
2411
+ const command: SubCommand = {
2412
+ name: "command",
2413
+ options: [{
2414
+ name: "foo",
2415
+ type: ArgumentValueTypeName.STRING,
2416
+ isArray: true,
2417
+ }],
2418
+ positionals: [],
2419
+ execute: async (): Promise<void> => {
2420
+ },
2421
+ };
2422
+
2423
+ const potentialArgs: Array<string> = [];
2424
+ for (let i = 0; i < 257; i++) {
2425
+ potentialArgs.push(`--foo=bar${i}`);
2426
+ }
2427
+ const values = { foo: [] as Array<string> };
2428
+ for (let i = 0; i < 255; i++) {
2429
+ values.foo.push(`bar${i}`);
2430
+ }
2431
+
2432
+ let result = populateSubCommandValues(
2433
+ command,
2434
+ potentialArgs,
2435
+ undefined,
2436
+ );
2437
+ expectExtractResult(result, values, ["--foo=bar256"]);
2438
+ expect(result.invalidArgument).toEqual({
2439
+ argument: command.options[0],
2440
+ reason: InvalidArgumentReason.ARRAY_SIZE_EXCEEDED,
2441
+ name: "foo",
2442
+ });
2443
+
2444
+ result = populateSubCommandValues(
2445
+ command,
2446
+ ["--foo[256]=bar"],
2447
+ undefined,
2448
+ );
2449
+ expectExtractResult(result, {}, []);
2450
+ expect(result.invalidArgument).toEqual({
2451
+ argument: command.options[0],
2452
+ reason: InvalidArgumentReason.ARRAY_SIZE_EXCEEDED,
2453
+ name: "foo",
2454
+ });
2455
+
2456
+ result = populateSubCommandValues(
2457
+ command,
2458
+ ["--foo[256]", "bar"],
2459
+ undefined,
2460
+ );
2461
+ expectExtractResult(result, {}, ["bar"]);
2462
+ expect(result.invalidArgument).toEqual({
2463
+ argument: command.options[0],
2464
+ reason: InvalidArgumentReason.ARRAY_SIZE_EXCEEDED,
2465
+ name: "foo",
2466
+ });
2467
+ });
2468
+
2469
+ test("Option array using mix of explicit and implicit indices", () => {
2470
+ const command: SubCommand = {
2471
+ name: "command",
2472
+ options: [{
2473
+ name: "foo",
2474
+ type: ArgumentValueTypeName.STRING,
2475
+ shortAlias: "f",
2476
+ isArray: true,
2477
+ }],
2478
+ positionals: [],
2479
+ execute: async (): Promise<void> => {
2480
+ },
2481
+ };
2482
+
2483
+ let result = populateSubCommandValues(
2484
+ command,
2485
+ ["--foo[0]", "bar0", "-f=bar1", "-f[2]=bar2"],
2486
+ undefined,
2487
+ );
2488
+ expectExtractResult(result, { foo: ["bar0", "bar1", "bar2"] }, []);
2489
+ expect(result.invalidArgument).toBeUndefined();
2490
+
2491
+ result = populateSubCommandValues(
2492
+ command,
2493
+ ["--foo", "bar0", "--foo", "bar1", "--foo[2]=bar2"],
2494
+ undefined,
2495
+ );
2496
+ expectExtractResult(result, { foo: ["bar0", "bar1", "bar2"] }, []);
2497
+ expect(result.invalidArgument).toBeUndefined();
2498
+
2499
+ result = populateSubCommandValues(
2500
+ command,
2501
+ ["--foo[2]", "bar2", "--foo", "bar3"],
2502
+ undefined,
2503
+ );
2504
+
2505
+ const values = { foo: [] as Array<string | undefined> };
2506
+ values.foo[2] = "bar2";
2507
+ values.foo[3] = "bar3";
2508
+
2509
+ expectExtractResult(result, values, []);
2510
+ expect(result.invalidArgument).toBeUndefined();
2511
+ });
2512
+
2513
+ test("Positional varargs check for maximum array size", () => {
2514
+ const command: SubCommand = {
2515
+ name: "command",
2516
+ options: [],
2517
+ positionals: [{
2518
+ name: "foo",
2519
+ type: ArgumentValueTypeName.STRING,
2520
+ isVarargMultiple: true,
2521
+ }],
2522
+ execute: async (): Promise<void> => {
2523
+ },
2524
+ };
2525
+
2526
+ const potentialArgs: Array<string> = [];
2527
+ for (let i = 0; i < 257; i++) {
2528
+ potentialArgs.push(`bar${i}`);
2529
+ }
2530
+ const values = { foo: [] as Array<string> };
2531
+ for (let i = 0; i < 255; i++) {
2532
+ values.foo.push(`bar${i}`);
2533
+ }
2534
+
2535
+ const result = populateSubCommandValues(
2536
+ command,
2537
+ potentialArgs,
2538
+ undefined,
2539
+ );
2540
+ expectExtractResult(result, values, ["bar256"]);
2541
+ expect(result.invalidArgument).toEqual({
2542
+ argument: command.positionals[0],
2543
+ reason: InvalidArgumentReason.ARRAY_SIZE_EXCEEDED,
2544
+ name: "foo",
2545
+ });
2546
+ });
2547
+
2548
+ test("Option path invalid reference to complex option", () => {
2549
+ const command: SubCommand = {
2550
+ name: "command",
2551
+ options: [{
2552
+ name: "alpha",
2553
+ type: ComplexValueTypeName.COMPLEX,
2554
+ shortAlias: "a",
2555
+ properties: [{
2556
+ name: "beta",
2557
+ type: ComplexValueTypeName.COMPLEX,
2558
+ shortAlias: "b",
2559
+ properties: [{
2560
+ name: "gamma",
2561
+ type: ArgumentValueTypeName.STRING,
2562
+ shortAlias: "g",
2563
+ }, {
2564
+ name: "delta",
2565
+ type: ArgumentValueTypeName.STRING,
2566
+ shortAlias: "d",
2567
+ }],
2568
+ }],
2569
+ }],
2570
+ positionals: [],
2571
+ execute: async (): Promise<void> => {
2572
+ },
2573
+ };
2574
+
2575
+ const result = populateSubCommandValues(
2576
+ command,
2577
+ ["--alpha.beta", "foo"],
2578
+ undefined,
2579
+ );
2580
+ expectExtractResult(result, {
2581
+ alpha: { beta: {} },
2582
+ }, ["foo"]);
2583
+ expect(result.invalidArgument).toEqual({
2584
+ argument: (command.options[0] as ComplexOption).properties[0],
2585
+ reason: InvalidArgumentReason.OPTION_IS_COMPLEX,
2586
+ name: "beta",
2587
+ });
2588
+ });
2589
+
2590
+ test("Complex options with duplicated names and aliases ok if not siblings", () => {
2591
+ const command: SubCommand = {
2592
+ name: "command",
2593
+ options: [{
2594
+ name: "alpha",
2595
+ type: ComplexValueTypeName.COMPLEX,
2596
+ shortAlias: "a",
2597
+ properties: [{
2598
+ name: "beta",
2599
+ type: ComplexValueTypeName.COMPLEX,
2600
+ shortAlias: "b",
2601
+ properties: [{
2602
+ name: "alpha",
2603
+ type: ArgumentValueTypeName.STRING,
2604
+ shortAlias: "a",
2605
+ }],
2606
+ }],
2607
+ }],
2608
+ positionals: [],
2609
+ execute: async (): Promise<void> => {
2610
+ },
2611
+ };
2612
+
2613
+ const result = populateSubCommandValues(
2614
+ command,
2615
+ ["-a.b.a", "foo"],
2616
+ undefined,
2617
+ );
2618
+ expectExtractResult(result, {
2619
+ alpha: { beta: { alpha: "foo" } },
2620
+ }, []);
2621
+ expect(result.invalidArgument).toBeUndefined();
2622
+ });
2623
+
2624
+ test("Option path check for maximum nesting", () => {
2625
+ const command: SubCommand = {
2626
+ name: "command",
2627
+ options: [{
2628
+ name: "alpha",
2629
+ type: ComplexValueTypeName.COMPLEX,
2630
+ shortAlias: "a",
2631
+ properties: [{
2632
+ name: "beta",
2633
+ type: ComplexValueTypeName.COMPLEX,
2634
+ shortAlias: "b",
2635
+ properties: [{
2636
+ name: "gamma",
2637
+ type: ArgumentValueTypeName.STRING,
2638
+ shortAlias: "g",
2639
+ }],
2640
+ }],
2641
+ }],
2642
+ positionals: [],
2643
+ execute: async (): Promise<void> => {
2644
+ },
2645
+ };
2646
+
2647
+ const result = populateSubCommandValues(
2648
+ command,
2649
+ ["-a.b.g.d.e.f.g.h.i.j.k", "foo"],
2650
+ undefined,
2651
+ );
2652
+ expectExtractResult(result, {}, ["foo"]);
2653
+ expect(result.invalidArgument).toEqual({
2654
+ reason: InvalidArgumentReason.NESTING_DEPTH_EXCEEDED,
2655
+ name: "-a.b.g.d.e.f.g.h.i.j.k",
2656
+ });
2657
+ });
2658
+ });