@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,766 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ getGlobalCommand,
4
+ getGlobalModifierCommand,
5
+ getSubCommandWithComplexOptions,
6
+ getSubCommandWithOptionAndPositional,
7
+ } from "../fixtures/Command.ts";
8
+ import {
9
+ parseGlobalCommandClause,
10
+ type ParseResult,
11
+ parseSubCommandClause,
12
+ } from "../../src/runtime/parser.ts";
13
+ import { InvalidArgumentReason } from "../../src/api/RunResult.ts";
14
+ import type ComplexOption from "../../src/api/argument/ComplexOption.ts";
15
+ import {
16
+ type ArgumentValueType,
17
+ ArgumentValueTypeName,
18
+ ComplexValueTypeName,
19
+ type PopulatedArgumentValues,
20
+ } from "../../src/api/argument/ArgumentValueTypes.ts";
21
+ import type SubCommand from "../../src/api/command/SubCommand.ts";
22
+
23
+ function expectParseResult(result: ParseResult, expected: ParseResult) {
24
+ expect(
25
+ result.populatedArgumentValues,
26
+ ).toEqual(
27
+ expected.populatedArgumentValues!,
28
+ );
29
+ expect(result.unusedArgs).toEqual(expected.unusedArgs);
30
+ expect(result.invalidArguments).toEqual(expected.invalidArguments);
31
+ }
32
+
33
+ describe("parser Tests", () => {
34
+ test("Arguments parsed for sub-command", () => {
35
+ const subCommand = getSubCommandWithOptionAndPositional();
36
+
37
+ let parseResult = parseSubCommandClause({
38
+ command: subCommand,
39
+ potentialArgs: ["bar", "--goo", "g"],
40
+ });
41
+ expectParseResult(parseResult, {
42
+ command: subCommand,
43
+ populatedArgumentValues: {
44
+ goo: "g",
45
+ foo: "bar",
46
+ },
47
+ unusedArgs: [],
48
+ invalidArguments: [],
49
+ });
50
+
51
+ parseResult = parseSubCommandClause({
52
+ command: subCommand,
53
+ potentialArgs: ["bar", "-g=g"],
54
+ });
55
+ expectParseResult(parseResult, {
56
+ command: subCommand,
57
+ populatedArgumentValues: {
58
+ goo: "g",
59
+ foo: "bar",
60
+ },
61
+ unusedArgs: [],
62
+ invalidArguments: [],
63
+ });
64
+ });
65
+
66
+ test("Quoted string parsing works", () => {
67
+ const subCommand = getSubCommandWithOptionAndPositional();
68
+
69
+ let parseResult = parseSubCommandClause({
70
+ command: subCommand,
71
+ potentialArgs: ["bar with more", "--goo", "g with more"],
72
+ });
73
+ expectParseResult(parseResult, {
74
+ command: subCommand,
75
+ populatedArgumentValues: {
76
+ goo: "g with more",
77
+ foo: "bar with more",
78
+ },
79
+ unusedArgs: [],
80
+ invalidArguments: [],
81
+ });
82
+
83
+ parseResult = parseSubCommandClause({
84
+ command: subCommand,
85
+ potentialArgs: ["bar with more", "-g=g with more"],
86
+ });
87
+ expectParseResult(parseResult, {
88
+ command: subCommand,
89
+ populatedArgumentValues: {
90
+ goo: "g with more",
91
+ foo: "bar with more",
92
+ },
93
+ unusedArgs: [],
94
+ invalidArguments: [],
95
+ });
96
+ });
97
+
98
+ test("Arguments parsed for global command", () => {
99
+ const globalCommand = getGlobalCommand("globalCommand", true);
100
+
101
+ const parseResult = parseGlobalCommandClause({
102
+ command: globalCommand,
103
+ potentialArgs: ["foo"],
104
+ });
105
+ expectParseResult(parseResult, {
106
+ command: globalCommand,
107
+ populatedArgumentValues: "foo",
108
+ unusedArgs: [],
109
+ invalidArguments: [],
110
+ });
111
+ });
112
+
113
+ test("Arguments parsed for global modifier command", () => {
114
+ const globalModifierCommand = getGlobalModifierCommand(
115
+ "modifierCommand",
116
+ "m",
117
+ true,
118
+ true,
119
+ );
120
+
121
+ const parseResult = parseGlobalCommandClause({
122
+ command: globalModifierCommand,
123
+ potentialArgs: ["foo"],
124
+ });
125
+ expectParseResult(parseResult, {
126
+ command: globalModifierCommand,
127
+ populatedArgumentValues: "foo",
128
+ unusedArgs: [],
129
+ invalidArguments: [],
130
+ });
131
+ });
132
+
133
+ test("Arguments parsed with trailing args", () => {
134
+ const subCommand = getSubCommandWithOptionAndPositional();
135
+
136
+ const parseResult = parseSubCommandClause({
137
+ command: subCommand,
138
+ potentialArgs: ["bar", "--goo", "g", "hello", "--world"],
139
+ });
140
+ expectParseResult(parseResult, {
141
+ command: subCommand,
142
+ populatedArgumentValues: {
143
+ goo: "g",
144
+ foo: "bar",
145
+ },
146
+ unusedArgs: ["hello", "--world"],
147
+ invalidArguments: [],
148
+ });
149
+ });
150
+
151
+ test("All arguments provided in config", () => {
152
+ const subCommand = getSubCommandWithOptionAndPositional();
153
+
154
+ const parseResult = parseSubCommandClause({
155
+ command: subCommand,
156
+ potentialArgs: [],
157
+ }, {
158
+ goo: "g",
159
+ foo: "bar",
160
+ });
161
+ expectParseResult(parseResult, {
162
+ command: subCommand,
163
+ populatedArgumentValues: {
164
+ goo: "g",
165
+ foo: "bar",
166
+ },
167
+ unusedArgs: [],
168
+ invalidArguments: [],
169
+ });
170
+ });
171
+
172
+ test("Some arguments provided in config", () => {
173
+ const subCommand = getSubCommandWithOptionAndPositional();
174
+
175
+ let parseResult = parseSubCommandClause({
176
+ command: subCommand,
177
+ potentialArgs: ["--goo", "g"],
178
+ }, {
179
+ foo: "bar",
180
+ });
181
+ expectParseResult(parseResult, {
182
+ command: subCommand,
183
+ populatedArgumentValues: {
184
+ goo: "g",
185
+ foo: "bar",
186
+ },
187
+ unusedArgs: [],
188
+ invalidArguments: [],
189
+ });
190
+
191
+ parseResult = parseSubCommandClause({
192
+ command: subCommand,
193
+ potentialArgs: ["bar"],
194
+ }, {
195
+ goo: "g",
196
+ });
197
+ expectParseResult(parseResult, {
198
+ command: subCommand,
199
+ populatedArgumentValues: {
200
+ goo: "g",
201
+ foo: "bar",
202
+ },
203
+ unusedArgs: [],
204
+ invalidArguments: [],
205
+ });
206
+ });
207
+
208
+ test("Extra config provided", () => {
209
+ const subCommand = getSubCommandWithOptionAndPositional();
210
+
211
+ const parseResult = parseSubCommandClause({
212
+ command: subCommand,
213
+ potentialArgs: ["--goo", "g"],
214
+ }, {
215
+ foo: "bar",
216
+ yee: "ha",
217
+ });
218
+ expectParseResult(parseResult, {
219
+ command: subCommand,
220
+ populatedArgumentValues: {
221
+ goo: "g",
222
+ foo: "bar",
223
+ yee: "ha",
224
+ },
225
+ unusedArgs: [],
226
+ invalidArguments: [],
227
+ });
228
+ });
229
+
230
+ test("Arguments parsed for sub-command with complex options", () => {
231
+ const subCommand = getSubCommandWithComplexOptions(false, true);
232
+
233
+ const parseResult = parseSubCommandClause({
234
+ command: subCommand,
235
+ potentialArgs: [
236
+ "-e.g=bar",
237
+ "-e.d=1",
238
+ "-a.b[1].g=foo2",
239
+ "--alpha.beta[0].gamma=foo1",
240
+ "-a.b[1].d[1]=2",
241
+ "-a.b[0].d=1",
242
+ "-a.b[1].d[0]=3",
243
+ ],
244
+ });
245
+ expectParseResult(parseResult, {
246
+ command: subCommand,
247
+ populatedArgumentValues: {
248
+ alpha: [
249
+ {
250
+ beta: [
251
+ {
252
+ delta: [
253
+ 1,
254
+ ],
255
+ gamma: "foo1",
256
+ },
257
+ {
258
+ delta: [
259
+ 3,
260
+ 2,
261
+ ],
262
+ gamma: "foo2",
263
+ },
264
+ ],
265
+ },
266
+ ],
267
+ epsilon: {
268
+ gamma: "bar",
269
+ delta: 1,
270
+ },
271
+ },
272
+ unusedArgs: [],
273
+ invalidArguments: [],
274
+ });
275
+ });
276
+
277
+ test("Arguments parsed for sub-command with complex options (illegal scenarios)", () => {
278
+ const subCommand = getSubCommandWithComplexOptions();
279
+
280
+ let parseResult = parseSubCommandClause({
281
+ command: subCommand,
282
+ potentialArgs: [
283
+ "-a.b.g=foo1",
284
+ "-a.b.d=1",
285
+ "-a.b.g=foo2",
286
+ "-a.b.d=2",
287
+ "-a.b.d=3",
288
+ "-e.g=bar",
289
+ "-e.d=1",
290
+ ],
291
+ });
292
+ expectParseResult(parseResult, {
293
+ command: subCommand,
294
+ populatedArgumentValues: {
295
+ alpha: [
296
+ {
297
+ beta: {
298
+ delta: [
299
+ "1",
300
+ ],
301
+ gamma: "foo1",
302
+ },
303
+ },
304
+ ],
305
+ },
306
+ unusedArgs: [
307
+ "-a.b.d=2",
308
+ "-a.b.d=3",
309
+ "-e.g=bar",
310
+ "-e.d=1",
311
+ ],
312
+ invalidArguments: [{
313
+ argument: ((subCommand.options[0] as ComplexOption)
314
+ .properties[0] as ComplexOption).properties[0],
315
+ reason: InvalidArgumentReason.ILLEGAL_MULTIPLE_VALUES,
316
+ name: "gamma",
317
+ }],
318
+ });
319
+
320
+ parseResult = parseSubCommandClause({
321
+ command: subCommand,
322
+ potentialArgs: [
323
+ "--alpha.beta.gamma=foo1",
324
+ "-a.b.d=1",
325
+ "-a.b.d=2",
326
+ "-a.b.d=3",
327
+ "-e.g=bar",
328
+ "-e.d=1",
329
+ "-e.d=2",
330
+ ],
331
+ });
332
+ expectParseResult(parseResult, {
333
+ command: subCommand,
334
+ populatedArgumentValues: {
335
+ alpha: [
336
+ {
337
+ beta: {
338
+ delta: [
339
+ "1",
340
+ "2",
341
+ "3",
342
+ ],
343
+ gamma: "foo1",
344
+ },
345
+ },
346
+ ],
347
+ epsilon: {
348
+ gamma: "bar",
349
+ delta: "1",
350
+ },
351
+ },
352
+ unusedArgs: [],
353
+ invalidArguments: [
354
+ {
355
+ name: "delta",
356
+ argument: (subCommand.options[1] as ComplexOption).properties[1],
357
+ reason: InvalidArgumentReason.ILLEGAL_MULTIPLE_VALUES,
358
+ },
359
+ ],
360
+ });
361
+ });
362
+
363
+ test("Arguments parsed for sub-command with complex options and sparse array", () => {
364
+ const subCommand = getSubCommandWithComplexOptions(false, true);
365
+
366
+ let parseResult = parseSubCommandClause({
367
+ command: subCommand,
368
+ potentialArgs: [
369
+ "-e.g=bar",
370
+ "-e.d=1",
371
+ "-a.b[1].g=foo2",
372
+ "-a.b[1].d[1]=2",
373
+ "-a.b[1].d[0]=3",
374
+ ],
375
+ });
376
+
377
+ let argumentValues: PopulatedArgumentValues = {
378
+ alpha: [
379
+ {
380
+ beta: [],
381
+ },
382
+ ],
383
+ epsilon: {
384
+ gamma: "bar",
385
+ delta: 1,
386
+ },
387
+ };
388
+
389
+ ((argumentValues.alpha as Array<PopulatedArgumentValues>)[0].beta as Array<
390
+ PopulatedArgumentValues
391
+ >)[1] = {
392
+ gamma: "foo2",
393
+ delta: ["3", "2"], // these are still stored as strings as the validation (and type conversion) failed fast
394
+ };
395
+
396
+ expectParseResult(parseResult, {
397
+ command: subCommand,
398
+ populatedArgumentValues: argumentValues,
399
+ unusedArgs: [],
400
+ invalidArguments: [
401
+ {
402
+ argument: (subCommand.options[0] as ComplexOption).properties[0],
403
+ name: "alpha[0].beta[0]",
404
+ reason: InvalidArgumentReason.ILLEGAL_SPARSE_ARRAY,
405
+ },
406
+ ],
407
+ });
408
+
409
+ parseResult = parseSubCommandClause({
410
+ command: subCommand,
411
+ potentialArgs: [
412
+ "-e.g=bar",
413
+ "-e.d=1",
414
+ "-a.b[1].g=foo2",
415
+ "--alpha.beta[0].gamma=foo1",
416
+ "-a.b[1].d[2]=2",
417
+ "-a.b[1].d[0]=0",
418
+ "-a.b[0].d=1",
419
+ ],
420
+ });
421
+
422
+ argumentValues = {
423
+ alpha: [{
424
+ beta: [
425
+ {
426
+ gamma: "foo1",
427
+ delta: ["1"], // this is still stored as strings as the validation (and type conversion) failed fast
428
+ },
429
+ {
430
+ gamma: "foo2",
431
+ delta: [],
432
+ },
433
+ ],
434
+ }],
435
+ epsilon: {
436
+ gamma: "bar",
437
+ delta: 1,
438
+ },
439
+ };
440
+
441
+ // these are still stored as strings as the validation (and type conversion) failed fast
442
+ (((argumentValues.alpha as Array<PopulatedArgumentValues>)[0].beta as Array<
443
+ PopulatedArgumentValues
444
+ >)[1].delta as Array<ArgumentValueType>)[0] = "0";
445
+ (((argumentValues.alpha as Array<PopulatedArgumentValues>)[0].beta as Array<
446
+ PopulatedArgumentValues
447
+ >)[1].delta as Array<ArgumentValueType>)[2] = "2";
448
+
449
+ expectParseResult(parseResult, {
450
+ command: subCommand,
451
+ populatedArgumentValues: argumentValues,
452
+ unusedArgs: [],
453
+ invalidArguments: [
454
+ {
455
+ argument: ((subCommand.options[0] as ComplexOption)
456
+ .properties[0] as ComplexOption).properties[1],
457
+ name: "alpha[0].beta[1].delta[1]",
458
+ reason: InvalidArgumentReason.ILLEGAL_SPARSE_ARRAY,
459
+ },
460
+ ],
461
+ });
462
+ });
463
+
464
+ test("Arguments parsed for sub-command with complex options and missing property values", () => {
465
+ const subCommand = getSubCommandWithComplexOptions(false, false);
466
+
467
+ const parseResult = parseSubCommandClause({
468
+ command: subCommand,
469
+ potentialArgs: [
470
+ "-a.b.g=foo1",
471
+ "-a.b.d=1",
472
+ "-a.b.d=2",
473
+ "-a.b.d=3",
474
+ "-e.d=1",
475
+ ],
476
+ });
477
+ expectParseResult(parseResult, {
478
+ command: subCommand,
479
+ populatedArgumentValues: {
480
+ alpha: [{
481
+ beta: {
482
+ gamma: "foo1",
483
+ delta: [1, 2, 3],
484
+ },
485
+ }],
486
+ epsilon: {
487
+ delta: "1",
488
+ },
489
+ },
490
+ unusedArgs: [],
491
+ invalidArguments: [
492
+ {
493
+ argument: (subCommand.options[1] as ComplexOption).properties[0],
494
+ name: "epsilon.gamma",
495
+ reason: InvalidArgumentReason.MISSING_VALUE,
496
+ },
497
+ ],
498
+ });
499
+ });
500
+
501
+ test("Arguments parsed for sub-command with complex options and some values provided by config", () => {
502
+ const subCommand = getSubCommandWithComplexOptions(false, true);
503
+
504
+ const parseResult = parseSubCommandClause({
505
+ command: subCommand,
506
+ potentialArgs: [
507
+ "-a.b[1].g=foo2",
508
+ "--alpha.beta[0].gamma=foo1",
509
+ "-a.b[1].d[1]=2",
510
+ "-a.b[0].d=1",
511
+ "-a.b[1].d[0]=3",
512
+ ],
513
+ }, {
514
+ epsilon: {
515
+ gamma: "bar",
516
+ delta: 1,
517
+ },
518
+ });
519
+ expectParseResult(parseResult, {
520
+ command: subCommand,
521
+ populatedArgumentValues: {
522
+ alpha: [
523
+ {
524
+ beta: [
525
+ {
526
+ delta: [
527
+ 1,
528
+ ],
529
+ gamma: "foo1",
530
+ },
531
+ {
532
+ delta: [
533
+ 3,
534
+ 2,
535
+ ],
536
+ gamma: "foo2",
537
+ },
538
+ ],
539
+ },
540
+ ],
541
+ epsilon: {
542
+ gamma: "bar",
543
+ delta: 1,
544
+ },
545
+ },
546
+ unusedArgs: [],
547
+ invalidArguments: [],
548
+ });
549
+ });
550
+
551
+ test("Arguments parsed for sub-command with complex options and some values provided by config but overridden", () => {
552
+ const subCommand = getSubCommandWithComplexOptions(false, true);
553
+
554
+ const parseResult = parseSubCommandClause({
555
+ command: subCommand,
556
+ potentialArgs: [
557
+ "-e.g=bar2",
558
+ "-e.d=2",
559
+ "-a.b[1].g=foo2",
560
+ "--alpha.beta[0].gamma=foo1",
561
+ "-a.b[1].d[1]=2",
562
+ "-a.b[0].d=1",
563
+ "-a.b[1].d[0]=3",
564
+ ],
565
+ }, {
566
+ epsilon: {
567
+ gamma: "bar",
568
+ delta: 1,
569
+ },
570
+ });
571
+ expectParseResult(parseResult, {
572
+ command: subCommand,
573
+ populatedArgumentValues: {
574
+ alpha: [
575
+ {
576
+ beta: [
577
+ {
578
+ delta: [
579
+ 1,
580
+ ],
581
+ gamma: "foo1",
582
+ },
583
+ {
584
+ delta: [
585
+ 3,
586
+ 2,
587
+ ],
588
+ gamma: "foo2",
589
+ },
590
+ ],
591
+ },
592
+ ],
593
+ epsilon: {
594
+ gamma: "bar2",
595
+ delta: 2,
596
+ },
597
+ },
598
+ unusedArgs: [],
599
+ invalidArguments: [],
600
+ });
601
+ });
602
+
603
+ test("Arguments parsed for sub-command with complex options and all values provided by config", () => {
604
+ const subCommand = getSubCommandWithComplexOptions(false, true);
605
+
606
+ const parseResult = parseSubCommandClause({
607
+ command: subCommand,
608
+ potentialArgs: [],
609
+ }, {
610
+ alpha: [
611
+ {
612
+ beta: [
613
+ {
614
+ delta: [
615
+ 1,
616
+ ],
617
+ gamma: "foo1",
618
+ },
619
+ {
620
+ delta: [
621
+ 3,
622
+ 2,
623
+ ],
624
+ gamma: "foo2",
625
+ },
626
+ ],
627
+ },
628
+ ],
629
+ epsilon: {
630
+ gamma: "bar",
631
+ delta: 1,
632
+ },
633
+ });
634
+ expectParseResult(parseResult, {
635
+ command: subCommand,
636
+ populatedArgumentValues: {
637
+ alpha: [
638
+ {
639
+ beta: [
640
+ {
641
+ delta: [
642
+ 1,
643
+ ],
644
+ gamma: "foo1",
645
+ },
646
+ {
647
+ delta: [
648
+ 3,
649
+ 2,
650
+ ],
651
+ gamma: "foo2",
652
+ },
653
+ ],
654
+ },
655
+ ],
656
+ epsilon: {
657
+ gamma: "bar",
658
+ delta: 1,
659
+ },
660
+ },
661
+ unusedArgs: [],
662
+ invalidArguments: [],
663
+ });
664
+ });
665
+
666
+ test("Arguments parsed for sub-command with complex options and all values provided by default value", () => {
667
+ const subCommand: SubCommand = {
668
+ name: "subCommand",
669
+ options: [{
670
+ name: "alpha",
671
+ shortAlias: "a",
672
+ type: ComplexValueTypeName.COMPLEX,
673
+ isArray: true,
674
+ defaultValue: [
675
+ {
676
+ beta: [
677
+ {
678
+ delta: [
679
+ 1,
680
+ ],
681
+ gamma: "foo1",
682
+ },
683
+ {
684
+ delta: [
685
+ 3,
686
+ 2,
687
+ ],
688
+ gamma: "foo2",
689
+ },
690
+ ],
691
+ },
692
+ ],
693
+ properties: [{
694
+ name: "beta",
695
+ shortAlias: "b",
696
+ type: ComplexValueTypeName.COMPLEX,
697
+ isArray: true,
698
+ properties: [{
699
+ name: "gamma",
700
+ shortAlias: "g",
701
+ type: ArgumentValueTypeName.STRING,
702
+ }, {
703
+ name: "delta",
704
+ shortAlias: "d",
705
+ type: ArgumentValueTypeName.NUMBER,
706
+ isArray: true,
707
+ }],
708
+ }],
709
+ }, {
710
+ name: "epsilon",
711
+ shortAlias: "e",
712
+ type: ComplexValueTypeName.COMPLEX,
713
+ defaultValue: {
714
+ gamma: "bar",
715
+ delta: 1,
716
+ },
717
+ properties: [{
718
+ name: "gamma",
719
+ shortAlias: "g",
720
+ type: ArgumentValueTypeName.STRING,
721
+ }, {
722
+ name: "delta",
723
+ shortAlias: "d",
724
+ type: ArgumentValueTypeName.NUMBER,
725
+ }],
726
+ }],
727
+ positionals: [],
728
+ execute: async (): Promise<void> => {},
729
+ };
730
+
731
+ const parseResult = parseSubCommandClause({
732
+ command: subCommand,
733
+ potentialArgs: [],
734
+ });
735
+ expectParseResult(parseResult, {
736
+ command: subCommand,
737
+ populatedArgumentValues: {
738
+ alpha: [
739
+ {
740
+ beta: [
741
+ {
742
+ delta: [
743
+ 1,
744
+ ],
745
+ gamma: "foo1",
746
+ },
747
+ {
748
+ delta: [
749
+ 3,
750
+ 2,
751
+ ],
752
+ gamma: "foo2",
753
+ },
754
+ ],
755
+ },
756
+ ],
757
+ epsilon: {
758
+ gamma: "bar",
759
+ delta: 1,
760
+ },
761
+ },
762
+ unusedArgs: [],
763
+ invalidArguments: [],
764
+ });
765
+ });
766
+ });