cli_annotations 0.1.0-dev.2
cli_annotations: ^0.1.0-dev.2 copied to clipboard
Annotations for generating CLI applications using `package:cli_gen`.
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
help
text 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_annotations
to yourpubspec
dependencies andcli_gen
andbuild_runner
as 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/ directory
You can optionally define an executable name and activate it using pub global activate.
Once dependencies are installed, start the
build_runner
to begin code generation.$ dart run build_runner watch -d
-
Create a
CommandRunner
by annotating a class with@cliRunner
and 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 themain
function.@cliRunner class GitRunner extends _$GitRunner { // ... }
-
Create a
Command
by 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
Subcommand
by annotating a class with@cliSubcommand
and 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
main
function that callsrun
on 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
CommandRunner
class - has a
run
method that should be passed args and run from yourmain
function - mounts any nested commands as subcommands via
CommandRunner.addCommand
- generates a
@cliCommand
- generates a
Command
class - overrides the
run
method with a call to your method or function
- generates a
@cliSubcommand
- generates a
Command
class - 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
-
help
comments from doc comments
-
-
Annotations to guide the generator and override default behavior
- β
@cliCommand
to override the generated command name - β
@cliSubcommand
to override the generated subcommand name - β
@cliRunner
to override the generated runner name - β
@mount
to mount a subcommand to a parent command - β
@cliOption
to provide access topackage:args
options
- β
-
Command generation
- β
Generate a
Command
class using a@cliCommand
Method or Function annotation - β
Generate a
Subcommand
class using a@cliSubcommand
Class annotation - β
Generate a
CommandRunner
using a@cliRunner
Class annotation- β
Allow mounting nested subcommands using a
@mount
annotation 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.