Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Terminates the process after `dispatch`'s `:help` option prints an *error*
Must exit or throw.

Default: `System/exit` (JVM), `js/process.exit` (Node), `throw` (browser).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1592-L1619">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1694-L1722">Source</a></sub></p>

## <a name="babashka.cli/apply-defaults">`apply-defaults`</a>
``` clojure
Expand All @@ -76,7 +76,7 @@ Fills missing keys in `m` from defaults. Existing keys in `m` win.
Supported options:
* `:exec-args` - map of defaults. Not subject to `:restrict`.
* `:spec` - spec; `:default` entries become defaults via `spec->opts`.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L456-L472">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L491-L507">Source</a></sub></p>

## <a name="babashka.cli/auto-coerce">`auto-coerce`</a>
``` clojure
Expand All @@ -89,7 +89,7 @@ Auto-coerces `s` to data. Does not coerce when `s` is not a string.
* is `true` or `false`, it is coerced as boolean
* starts with number, it is coerced as a number (through Clojure's `edn/read-string`)
* starts with `:`, it is coerced as a keyword (through [`parse-keyword`](#babashka.cli/parse-keyword))
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L78-L104">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L89-L120">Source</a></sub></p>

## <a name="babashka.cli/coerce">`coerce`</a>
``` clojure
Expand All @@ -101,7 +101,7 @@ Coerce string `s` using `f`. Does not coerce when `s` is not a string.
`f` may be a keyword (`:boolean`, `:int`, `:double`, `:symbol`,
`:keyword`) or a function. When `f` return `nil`, this is
interpreted as a parse failure and throws.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L150-L156">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L174-L180">Source</a></sub></p>

## <a name="babashka.cli/coerce-opts">`coerce-opts`</a>
``` clojure
Expand All @@ -123,7 +123,7 @@ Coerces values in the map `m` using the provided configuration.
`"--foo"`, `"-f"`, or `":foo"`), as opposed to `:option`, the normalized
keyword (`:foo`). It lets a handler echo what the user actually typed rather
than reconstruct it. It is omitted when no originating token is known.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L286-L361">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L317-L396">Source</a></sub></p>

## <a name="babashka.cli/default-width-fn">`default-width-fn`</a>
``` clojure
Expand All @@ -136,7 +136,7 @@ The default `:max-width-fn` for [`format-table`](#babashka.cli/format-table)/[`f
width or nil: node `process.stdout.columns`, else `$COLUMNS`, else a JLine
provider probe (clj, when JLine is on the classpath, e.g. babashka), else nil
(the caller then falls back to 80).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L728-L740">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L816-L834">Source</a></sub></p>

## <a name="babashka.cli/dispatch">`dispatch`</a>
``` clojure
Expand Down Expand Up @@ -209,7 +209,7 @@ Command dispatcher.
Each entry in the table may have additional [`parse-args`](#babashka.cli/parse-args) options.

For more information and examples, see [README.md](README.md#commands).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1905-L1993">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L2023-L2111">Source</a></sub></p>

## <a name="babashka.cli/format-command-error">`format-command-error`</a>
``` clojure
Expand All @@ -233,7 +233,7 @@ Render a terse, helpful message (a string) for a dispatch error.
this, then calls [`*exit-fn*`](#babashka.cli/*exit-fn*)). Call it from a custom `:error-fn` to keep the
standard message and add your own output. `--help`/`-h` is not an error - it
goes to the `:help-fn`, rendered by [`format-command-help`](#babashka.cli/format-command-help).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1630-L1677">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1733-L1780">Source</a></sub></p>

## <a name="babashka.cli/format-command-help">`format-command-help`</a>
``` clojure
Expand Down Expand Up @@ -278,7 +278,7 @@ Render conventional `--help` text (a string) for the command at path `cmds`
This is the renderer the `:help` option uses; call it from a custom `:help-fn`
to render the standard help and then add your own output. An entry may carry
`:no-doc true` to be omitted from `Commands:`.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1549-L1590">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1651-L1692">Source</a></sub></p>

## <a name="babashka.cli/format-opts">`format-opts`</a>
``` clojure
Expand All @@ -289,7 +289,7 @@ Function.
Formats options into an options usage help string.

See [Printing options](/README.md#printing-options).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L904-L915">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L998-L1009">Source</a></sub></p>

## <a name="babashka.cli/format-table">`format-table`</a>
``` clojure
Expand All @@ -302,7 +302,7 @@ Function.

Formats `rows` into a table (string).
See [Printing options](/README.md#printing-options).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L793-L815">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L887-L909">Source</a></sub></p>

## <a name="babashka.cli/merge-opts">`merge-opts`</a>
``` clojure
Expand All @@ -311,7 +311,7 @@ Formats `rows` into a table (string).
Function.

Merges babashka CLI options.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L12-L15">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L17-L20">Source</a></sub></p>

## <a name="babashka.cli/opts->table">`opts->table`</a>
``` clojure
Expand All @@ -321,7 +321,7 @@ Function.

Converts options to a table of rows.
See [Printing options](/README.md#printing-options).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L845-L868">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L939-L962">Source</a></sub></p>

## <a name="babashka.cli/parse-args">`parse-args`</a>
``` clojure
Expand All @@ -335,7 +335,7 @@ Same as [`parse-opts`](#babashka.cli/parse-opts) with return data reshaped.
Returns a map with:
* `:opts` parsed opts
* `:args` remaining unparsed `args`
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L669-L679">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L757-L767">Source</a></sub></p>

## <a name="babashka.cli/parse-cmds">`parse-cmds`</a>
``` clojure
Expand All @@ -347,7 +347,7 @@ Function.
Parses sub-commands (arguments not starting with an option prefix). Returns a map with:
* `:cmds` - The parsed commands
* `:args` - The remaining (unparsed) arguments
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L201-L211">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L229-L239">Source</a></sub></p>

## <a name="babashka.cli/parse-keyword">`parse-keyword`</a>
``` clojure
Expand All @@ -356,7 +356,7 @@ Parses sub-commands (arguments not starting with an option prefix). Returns a ma
Function.

Parse keyword from `s`. Ignores leading `:`.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L66-L71">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L77-L82">Source</a></sub></p>

## <a name="babashka.cli/parse-opts">`parse-opts`</a>
``` clojure
Expand Down Expand Up @@ -395,7 +395,7 @@ Returns a map of options parsed from command line arguments `args`, a seq of str
;; => throws 'Unknown option --qux' exception b/c there is no :qux key in the spec
```
See also: [`parse-args`](#babashka.cli/parse-args)
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L621-L667">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L709-L755">Source</a></sub></p>

## <a name="babashka.cli/parse-opts*">`parse-opts*`</a>
``` clojure
Expand All @@ -406,7 +406,7 @@ Function.
Parses CLI `args` into a raw opts map. Returns string values unchanged
(no coercion), does not apply `:exec-args` defaults, does not run
`:restrict`/`:require`/`:validate`. Result map includes
`:org.babashka/cli` metadata and internal `::implicit-true-keys` /
`:org.babashka/cli` metadata and internal `::implicit-values` /
`::keys-order` metadata used by `coerce-opts`.

Use this when you want to merge other sources (e.g. config files)
Expand All @@ -415,7 +415,7 @@ Parses CLI `args` into a raw opts map. Returns string values unchanged

Supported options (subset of `parse-opts`): `:alias`/`:aliases`, `:coerce`,
`:collect`, `:no-keyword-opts`, `:repeated-opts`, `:args->opts`, `:spec`.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L478-L619">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L512-L707">Source</a></sub></p>

## <a name="babashka.cli/spec->opts">`spec->opts`</a>
``` clojure
Expand All @@ -425,7 +425,7 @@ Parses CLI `args` into a raw opts map. Returns string values unchanged
Function.

Converts spec into opts format. Pass existing opts as optional second argument.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L178-L199">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L206-L227">Source</a></sub></p>

## <a name="babashka.cli/table->tree">`table->tree`</a>
``` clojure
Expand All @@ -445,7 +445,7 @@ Converts a `dispatch` table into a tree. Each `:cmds` becomes a path of
```

A tree passed in is normalized and returned, so the function is idempotent.
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1076-L1098">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L1170-L1192">Source</a></sub></p>

## <a name="babashka.cli/validate-opts">`validate-opts`</a>
``` clojure
Expand All @@ -469,7 +469,7 @@ Validates the map `m` using the provided configuration. Returns `m`.
keyword (`:foo`). It lets a handler echo what the user actually typed rather
than reconstruct it. It is present for `:restrict` and `:validate`, and absent
for `:require` (a missing required option was never typed, so it has no token).
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L363-L454">Source</a></sub></p>
<p><sub><a href="https://github.com/babashka/cli/blob/main/src/babashka/cli.cljc#L398-L489">Source</a></sub></p>

-----
# <a name="babashka.cli.exec">babashka.cli.exec</a>
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ For breaking changes, check [here](#breaking-changes).

[Babashka CLI](https://github.com/babashka/cli): turn Clojure functions into CLIs!

## Unreleased

- [#174](https://github.com/babashka/cli/issues/174): `:edn` `:coerce` option must provide an explicit value
([@lread](https://github.com/lread))

## v0.12.75 (2026-06-25)

- Squint support (v0.14.196+)
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,19 @@ It automatically tries to convert booleans, numbers, and keywords.
;; :str ["bar" java.lang.String]}
```

### Coercion to EDN

`:edn` coercion behaves like Clojure CLI `-X` coercion:

```clojure
(cli/parse-opts ["--foo" ":some-keyword"] {:spec {:foo {:coerce :edn}}})
;; => {:foo :some-keyword}
(cli/parse-opts ["--foo" "some-symbol"] {:spec {:foo {:coerce :edn}}})
;; => {:foo some-symbol}
(cli/parse-opts ["--foo" "\"some-string\""] {:spec {:foo {:coerce :edn}}})
;; => {:foo "some-string"}
```

### Aliases

An `:alias` specifies a synonym short option name for the option name.
Expand Down
5 changes: 4 additions & 1 deletion src/babashka/cli.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@
:symbol symbol
:keyword parse-keyword
:string identity
:edn edn/read-string
:edn (fn [s]
(when (some? implicit-value)
(throw-coerce s implicit-value f nil))
(edn/read-string s))
:auto auto-coerce
;; default
f)
Expand Down
19 changes: 17 additions & 2 deletions test/babashka/cli_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@
(cli/parse-opts [":foo"] {:coerce {:foo :string}})))
(is (thrown-with-msg?
#?(:cljd Object :default Exception) #"Missing value for option :foo"
(cli/parse-opts [":foo"] {:coerce {:foo [:string]}}))))
(cli/parse-opts [":foo"] {:coerce {:foo [:string]}})))
(is (thrown-with-msg?
#?(:cljd Object :default Exception) #"Missing value for option :foo"
(cli/parse-opts [":foo"] {:coerce {:foo :edn}})))
(is (thrown-with-msg?
#?(:cljd Object :default Exception) #"Missing value for option :foo"
(cli/parse-opts [":foo"] {:coerce {:foo [:edn]}}))))
(testing "composite opts"
(is (= {:a true, :b true, :c true, :foo true}
(cli/parse-opts ["--foo" "-abc"]))))
Expand All @@ -112,7 +118,7 @@
(is (= {:opts {:option false}}
(cli/parse-args [":no-option"]))))
(testing "--no-foo throws on non-boolean"
(doseq [coerce-fn [:long :number :symbol :keyword :string]]
(doseq [coerce-fn [:long :number :symbol :keyword :string :edn]]
(is (thrown-with-msg?
#?(:cljd Object :default Exception) #"Cannot negate option --foo"
(cli/parse-opts ["--no-foo"] {:coerce {:foo coerce-fn}}))
Expand All @@ -130,6 +136,15 @@
(cli/parse-opts [":no-foo"] {:coerce {:foo [coerce-fn]}}))
(str "for coerce to: [" coerce-fn "]")))))

(deftest coerce-to-edn-test
(testing "explicits aren't confused with implicits"
(is (= {:foo nil} (cli/parse-opts ["--foo" "nil"] {:coerce {:foo :edn}})))
(is (= {:foo true} (cli/parse-opts ["--foo" "true"] {:coerce {:foo :edn}})))
(is (= {:foo false} (cli/parse-opts ["--foo" "false"] {:coerce {:foo :edn}}))))
(testing "show behaviour is just like clojure CLI -X"
(is (= {:foo "string"} (cli/parse-opts ["-foo" "\"string\""] {:coerce {:foo :edn}})))
(is (= {:foo 'some-symbol} (cli/parse-opts ["-foo" "some-symbol"] {:coerce {:foo :edn}})))))

(deftest equals-value-test
(testing "--opt=val splits on the first = only"
(is (= {:header "k=v"} (cli/parse-opts ["--header=k=v"])))
Expand Down
Loading