Skip to content

Commit ca998f9

Browse files
authored
Fix Argument.optional() not working for positional arguments (#857)
1 parent a635222 commit ca998f9

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

packages/effect/src/unstable/cli/Param.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,9 @@ export const optional = <Kind extends ParamKind, A>(
10861086
Effect.map(
10871087
([leftover, value]) => [leftover, Option.some(value)] as const
10881088
),
1089-
Effect.catchTag("MissingOption", () => Effect.succeed([args.arguments, Option.none()] as const))
1089+
// Catch both MissingOption (for flags) and MissingArgument (for positional arguments)
1090+
Effect.catchTag("MissingOption", () => Effect.succeed([args.arguments, Option.none()] as const)),
1091+
Effect.catchTag("MissingArgument", () => Effect.succeed([args.arguments, Option.none()] as const))
10901092
)
10911093
return Object.assign(Object.create(Proto), {
10921094
_tag: "Optional",

packages/effect/test/unstable/cli/Arguments.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,37 @@ describe("Command arguments", () => {
322322
const helpText = stdout.join("\n")
323323
assert.isTrue(helpText.includes("FILE_PATH"))
324324
}).pipe(Effect.provide(TestLayer)))
325+
326+
it("should handle optional arguments - when provided", () =>
327+
Effect.gen(function*() {
328+
const resultRef = yield* Ref.make<any>(null)
329+
330+
const testCommand = Command.make("test", {
331+
label: Argument.optional(
332+
Argument.string("label").pipe(
333+
Argument.withDescription("Optional label name")
334+
)
335+
)
336+
}, (config) => Ref.set(resultRef, config))
337+
338+
// When optional argument IS provided
339+
yield* Command.runWith(testCommand, { version: "1.0.0" })(["my-label"])
340+
const result = yield* Ref.get(resultRef)
341+
assert.isTrue(Option.isSome(result.label))
342+
assert.strictEqual(result.label.value, "my-label")
343+
}).pipe(Effect.provide(TestLayer)))
344+
345+
it.effect("should handle optional arguments - when not provided", () =>
346+
Effect.gen(function*() {
347+
// BUG TEST: Argument.optional() should work for positional arguments
348+
// Currently it only catches MissingOption, not MissingArgument
349+
const optionalArg = Argument.optional(Argument.string("label"))
350+
351+
// Parse with empty arguments - should succeed with Option.none()
352+
const result = yield* optionalArg.parse({ flags: {}, arguments: [] })
353+
354+
// If we get here without error, optional is working
355+
const [_leftover, value] = result
356+
assert.isTrue(Option.isNone(value), "Should be Option.none()")
357+
}).pipe(Effect.provide(TestLayer)))
325358
})

0 commit comments

Comments
 (0)