cli_gen 0.1.0-dev.2
cli_gen: ^0.1.0-dev.2 copied to clipboard
Build cli applications using code generation and macros.
cli-gen #
🚧 This package is in early preview and is subject to API changes.
Build CLI applications from plain Dart classes and functions.
Motivation #
The ability to quickly whip up a command line script or application is a powerful skill for a developer to have. Unlike the Dart language itself, which offers a tremendous developer experience when building many kinds of apps, cli-based Dart libraries like package:args leave a lot to desire when it comes to getting up and running quickly, and being able to focus on building application logic instead of parsing and managing dynamic string data.
cli-gen aims to offer quality-of-life improvements for building and maintaining CLI apps, by allowing you to generate command line APIs from plain Dart functions. It does so by providing the following features:
- type-safe arguments via generated string parsing
helptext generation from param names, doc comments, and default values- proper error handling, without printing stack traces to the console
cli-gen was designed to make writing CLI applications as intuitive as writing any other piece of Dart code, by abstracting away the underlying package:args semantics while providing access to the underlying details, if ever necessary.
| Before | After |
|---|---|
Table of Contents #
Quick Start #
-
Add
cli_annotationsto yourpubspecdependencies andcli_genandbuild_runneras dev dependencies.name: dart_git description: An implementation of the git CLI in Dart. environment: sdk: ^3.0.0 dependencies: cli_annotations: ^0.1.0-dev.1 dev_dependencies: build_runner: ^2.4.8 cli_gen: ^0.1.0-dev.1 # define an executable name (optional) executables: dart_git: path: main # file name of `main()` in bin/ directoryYou can optionally define an executable name and activate it using pub global activate.
Once dependencies are installed, start the
build_runnerto begin code generation.$ dart run build_runner watch -d -
Create a
CommandRunnerby annotating a class with@cliRunnerand extending the generated superclass (uses the typical_$prefix).The generated code contains a single
CommandRunner.run()method, which is the entry point for your CLI application and will be called from themainfunction.@cliRunner class GitRunner extends _$GitRunner { // ... } -
Create a
Commandby simply creating a method on the class. Any primative type or enum will be automatically parsed from incoming string arguments.@cliRunner class GitRunner extends _$GitRunner { @cliCommand Future<void> merge({ required String branch, MergeStrategy strategy = MergeStrategy.ort, bool? commit, }) async { // ... application logic ... } } -
As your application grows, you'll begin to want to separate your commands into their own groups.
You can create a
Subcommandby annotating a class with@cliSubcommandand extending the generated superclass.// Create your subcommand @cliSubcommand class StashSubcommand extends _$StashSubcommand { @cliCommand Future<void> push() async { /* ... */ } @cliCommand Future<void> pop() async { /* ... */ } } // Then mount it to your `CommandRunner` or a parent `Subcommand` @cliRunner class GitRunner extends _$GitRunner { @mount Command get stash => StashSubcommand(); } -
Finally, create a
mainfunction that callsrunon yourCommandRunner.void main(List<String> arguments) async { final runner = GitRunner(); await runner.run(arguments); }
Your application is ready to go! Run a command to test out the generated help text and see the argument parsing in action.
# activate the executable (if executable is defined in `pubspec.yaml`)
$ dart pub global activate . --source=path
# run the application
$ dart_git merge --help
You should see the following output:
$ dart_git merge --help
Join two or more development histories together.
Usage: git-runner merge [arguments]
--branch (mandatory)
--commit
--options
Run "git-runner help" to see global options.
Under the Hood #
cli-gen generates code that uses package:args classes and utilities to manage command hierarchies and help text generation. The annotations included with this package are a 1:1 mapping to similar or identical concepts included with package:args, for example:
@cliRunner- generates a
CommandRunnerclass - has a
runmethod that should be passed args and run from yourmainfunction - mounts any nested commands as subcommands via
CommandRunner.addCommand
- generates a
@cliCommand- generates a
Commandclass - overrides the
runmethod with a call to your method or function
- generates a
@cliSubcommand- generates a
Commandclass - adds all nested commands as subcommands via
Command.addSubcommand
- generates a
Examples of generated code can be found in the example project, within their respective .g.dart files.
Features #
-
Arg parser generation from Parameters
-
Generate from a Constructor or Method/Function signature
-
Auto Argument Parsing (convert a String/bool argument into the expected Dart type, without using annotations to tell the builder what parser to use):
- ✅ Primatives: String, int, double, bool, Uri, DateTime
- ✅ Collections: List, Set, Iterable
- ❌ Map
- ✅ User-Defined types: Enums
- ❌ Classes
- ❌ Extension Types
-
Multi-Select arguments:
- ✅ List of primative values
- ✅ enums for a finite list of values
-
helpcomments from doc comments
-
-
Annotations to guide the generator and override default behavior
- ❌
@cliCommandto override the generated command name - ❌
@cliSubcommandto override the generated subcommand name - ❌
@cliRunnerto override the generated runner name - ❌
@mountto mount a subcommand to a parent command - ❌
@cliOptionto provide access topackage:argsoptions
- ❌
-
Command generation
- ✅ Generate a
Commandclass using a@cliCommandMethod or Function annotation - ✅ Generate a
Subcommandclass using a@cliSubcommandClass annotation - ✅ Generate a
CommandRunnerusing a@cliRunnerClass annotation- ✅ Allow mounting nested subcommands using a
@mountannotation on a Getter or Method
- ✅ Allow mounting nested subcommands using a
- ✅ Generate a
Design Goals #
TODO: write a little blurb about the goals (incl. what cli-gen is and what it is not).
Inspiration #
Several projects were researched as references of CLI ergonomics and macro libraries, including:
- clap - a declarative CLI parser for Rust
License #
cli-gen is released under the MIT License. See LICENSE for details.

