Merge pull request #2 from dart-lang/merge-args-package
* Split the args tests into a couple of smaller suites.
Review URL: https://codereview.chromium.org//11817027
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16882 260f80e4-7a28-3924-810f-c04153c831b5
* Add command support to args.
Review URL: https://codereview.chromium.org//11819068
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16988 260f80e4-7a28-3924-810f-c04153c831b5
* Added documentation links to all of the pkg packages hosted on api.dartlang.org.
Review URL: https://codereview.chromium.org//12218119
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18414 260f80e4-7a28-3924-810f-c04153c831b5
* Update documentation links to point to the correct URLs.
Review URL: https://codereview.chromium.org//12252055
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18552 260f80e4-7a28-3924-810f-c04153c831b5
* Remove deprecated Strings class.
Review URL: https://codereview.chromium.org//12295014
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18686 260f80e4-7a28-3924-810f-c04153c831b5
* Fix use of stringbuffer in pkg/args
Review URL: https://codereview.chromium.org//12335035
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18902 260f80e4-7a28-3924-810f-c04153c831b5
* Change new List(n) to return fixed length list.
Deprecate List.fixedLength, add List.filled.
Make Iterable.toList and List.from take "growable" argument,
defaulting to false.
Review URL: https://codereview.chromium.org//12328104
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19112 260f80e4-7a28-3924-810f-c04153c831b5
* Make arg parser return growable list of rest arguments.
This allows the user to remove them when he's done, which is what
is currently being done.
Review URL: https://codereview.chromium.org//12316155
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19119 260f80e4-7a28-3924-810f-c04153c831b5
* Make List.from and Iterable.toList default to not growable.
Review URL: https://codereview.chromium.org//12401002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19391 260f80e4-7a28-3924-810f-c04153c831b5
* Rename XMatching to XWhere.
For example firstMatching -> firstWhere.
BUG= http://dartbug.com/8664
BUG= http://dartbug.com/8337
Review URL: https://codereview.chromium.org//12537009
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19880 260f80e4-7a28-3924-810f-c04153c831b5
* pkg/args Option should be more strict about names and abbreviations
Should not allow: space, \t, \r, \n, single- and double-quote, slashes
Should not allow empty string
Should not allow '-' as the beginning character
name should not be allowed to be null (although it's fine for abbreviation)
BUG=https://code.google.com/p/dart/issues/detail?id=8965
Review URL: https://codereview.chromium.org//12472019
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@20040 260f80e4-7a28-3924-810f-c04153c831b5
* Moved pkg pubspecs to use dev_dependencies where appropriate
BUG=https://code.google.com/p/dart/issues/detail?id=9309
Review URL: https://codereview.chromium.org//12962003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@20285 260f80e4-7a28-3924-810f-c04153c831b5
* Remove Collection, Collections and clean up List/Set/Queue implementations of retain/remove.
BUG=
Review URL: https://codereview.chromium.org//14173003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@21338 260f80e4-7a28-3924-810f-c04153c831b5
* add installation instructions to pkg packages
BUG=
Review URL: https://codereview.chromium.org//14188048
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@21770 260f80e4-7a28-3924-810f-c04153c831b5
* Allow passing in an existing ArgParser for a command.
R=gram@google.com
Review URL: https://codereview.chromium.org//15621002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@22973 260f80e4-7a28-3924-810f-c04153c831b5
* Switch from DRT to content shell.
R=kustermann@google.com, ricow@google.com
Review URL: https://codereview.chromium.org//15755017
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@23328 260f80e4-7a28-3924-810f-c04153c831b5
* Moved the Option class to its own file in anticipation of working on it.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//16802002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24107 260f80e4-7a28-3924-810f-c04153c831b5
* Added new style of tests for parser.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//17333004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24207 260f80e4-7a28-3924-810f-c04153c831b5
* Added the continueParsing option to ArgParser.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//12545013
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24366 260f80e4-7a28-3924-810f-c04153c831b5
* pkg/args: expose immutable collections
BUG= https://code.google.com/p/dart/issues/detail?id=10127
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//19810004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@25347 260f80e4-7a28-3924-810f-c04153c831b5
* Copyedit pass over args library description; fixed some example code
R=mcampione@google.com, rnystrom@google.com
Review URL: https://codereview.chromium.org//23603002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@27388 260f80e4-7a28-3924-810f-c04153c831b5
* Add support for hidden options to pkg/args.
R=rnystrom@google.com
BUG=
Review URL: https://codereview.chromium.org//23913009
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@27853 260f80e4-7a28-3924-810f-c04153c831b5
* Remove uses of Options from pkg, samples, tests, and third_party directories.
BUG=
R=sgjesse@google.com
Review URL: https://codereview.chromium.org//52573002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29552 260f80e4-7a28-3924-810f-c04153c831b5
* add versions and constraints for packages and samples
- all packages at 0.9.0, except "analyzer" which had a version already
- dependencies at ">=0.9.0 <0.10.0" except analyzer is ">=0.10.0 <0.11.0"
- sdk constraint ">=1.0.0 <2.0.0"
R=sigmund@google.com
Review URL: https://codereview.chromium.org//59763006
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29957 260f80e4-7a28-3924-810f-c04153c831b5
* Revert "add versions and constraints for packages and samples"
This is currently blocking us from testing samples.
BUG=
R=kasperl@google.com
Review URL: https://codereview.chromium.org//59513007
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29960 260f80e4-7a28-3924-810f-c04153c831b5
* Re-land r29957 (add versions and constraints for packages and samples), with
SDK constraints bumped from 1.0.0 to 0.8.10+6 .
R=ricow@google.com, sigmund@google.com
Review URL: https://codereview.chromium.org//62473002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29986 260f80e4-7a28-3924-810f-c04153c831b5
* Combine unmodifiable_collection package into collection_helpers.
Avoids some duplicated code, and makes everything much simpler.
R=floitsch@google.com
Review URL: https://codereview.chromium.org//70073003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30402 260f80e4-7a28-3924-810f-c04153c831b5
* pkg/args: a bunch of test cleanup
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//88553003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30675 260f80e4-7a28-3924-810f-c04153c831b5
* Add a [hide] argument to [ArgParser.addFlag].
Also bump the args version to 0.10.0 in preperation for a release.
R=rnystrom@google.com
BUG=
Review URL: https://codereview.chromium.org//82213002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30678 260f80e4-7a28-3924-810f-c04153c831b5
* pkg/args: updated args dependency on collection_helpers to >= 0.9.1
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//89173002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30924 260f80e4-7a28-3924-810f-c04153c831b5
* Create associated packages for the dart:collection and dart:async libs.
R=sgjesse@google.com
Review URL: https://codereview.chromium.org//113883002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31260 260f80e4-7a28-3924-810f-c04153c831b5
* flagging pkg/args and pkg/logging as dev+1
should be publishing with bumped version due to package constraint changes
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//102833012
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31388 260f80e4-7a28-3924-810f-c04153c831b5
* pkg/args: adding LICENSE file
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//121753002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31391 260f80e4-7a28-3924-810f-c04153c831b5
* Ignore hidden options when calculating usage columns.
R=nweiz@google.com
Review URL: https://codereview.chromium.org//203273013
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@34140 260f80e4-7a28-3924-810f-c04153c831b5
* Move allowTrailingOptions into ArgParser.
R=brianwilkerson@google.com, jmesserly@google.com, nweiz@google.com
Review URL: https://codereview.chromium.org//260963007
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@35837 260f80e4-7a28-3924-810f-c04153c831b5
* Modernize the docs for args.
R=kevmoo@google.com
Review URL: https://codereview.chromium.org//271183002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@36072 260f80e4-7a28-3924-810f-c04153c831b5
* Fix typo in readme.
BUG=https://code.google.com/p/dart/issues/detail?id=19106
R=nweiz@google.com
Review URL: https://codereview.chromium.org//362383002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@37962 260f80e4-7a28-3924-810f-c04153c831b5
* Allow defining a help string for an option's parameter value.
BUG= https://code.google.com/p/dart/issues/detail?id=5201
R=nweiz@google.com
Review URL: https://codereview.chromium.org//363083002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38047 260f80e4-7a28-3924-810f-c04153c831b5
* Add .wasParsed() to ArgResults.
BUG=https://code.google.com/p/dart/issues/detail?id=16227
R=alanknight@google.com, nweiz@google.com, sigmund@google.com
Review URL: https://codereview.chromium.org//383913003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38348 260f80e4-7a28-3924-810f-c04153c831b5
* Remove documentation links for repo packages.
pub.dartlang.org will now automatically link to dartdocs.org for
packages with no documentation link.
BUG= http://dartbug.com/20177, http://dartbug.com/20178, http://dartbug.com/20186
R=sigmund@google.com
Review URL: https://codereview.chromium.org//416973003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38551 260f80e4-7a28-3924-810f-c04153c831b5
* Update dart:collection to version 1.0.0
R=sgjesse@google.com
Review URL: https://codereview.chromium.org//463333004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@39484 260f80e4-7a28-3924-810f-c04153c831b5
* Clean up after r39484 and r39486.
In particular, this adds CHANGELOG entries for packages that have been
modified and fixes pub's barback dependency so it doesn't declare
compatibility on an uncut release.
This releases args, barback, http_parser, and shelf.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//506973003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@39563 260f80e4-7a28-3924-810f-c04153c831b5
* Fix args README
BUG= https://code.google.com/p/dart/issues/detail?id=20224
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//634853002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@40965 260f80e4-7a28-3924-810f-c04153c831b5
* Swap getUsage() for get usage in args package
BUG= https://code.google.com/p/dart/issues/detail?id=12536
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//631123002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@40966 260f80e4-7a28-3924-810f-c04153c831b5
* Remove "-dev" to publish args package.
R=nweiz@google.com, paulberry@google.com
Review URL: https://codereview.chromium.org//698463004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@41448 260f80e4-7a28-3924-810f-c04153c831b5
* Add an ArgResults.original getter.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//796593004
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42301 260f80e4-7a28-3924-810f-c04153c831b5
* Add a CommandRunner class for dispatching commands to args.
This was extracted from pub's dispatching logic.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//797473002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42418 260f80e4-7a28-3924-810f-c04153c831b5
* Fix the built-in help command in args.
R=rnystrom@google.com
TBR
Review URL: https://codereview.chromium.org//812743002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42421 260f80e4-7a28-3924-810f-c04153c831b5
* Add gitignore, status, and codereview files.
* Don't mark tests as failing on the browser.
* fix dependency on unittest
R=sigmund@google.com
Review URL: https://codereview.chromium.org//849983002
* format code, removed unused variables and deprecated usage
R=sigmund@google.com
Review URL: https://codereview.chromium.org//849023002
* Fixed implementation of ArgResults.options to really use Iterable<String> instead of Iterable<dynamic> cast to Iterable<String>.
BUG=
R=sigmund@google.com
Review URL: https://codereview.chromium.org//870883006
* Explicit example of parsing args from main
* reflowing
* Update CHANGELOG and pubspec version.
* Are you a fan of method cascades?
* Fix example
addFlag doesn't return an instance.
* Fixed missing comma in test and updated README syntax
Closes dart-lang/args#1
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//925003002
* Remove dependency on 'collection'
* Update the CHANGELOG and pubspec.
* Fix the CHANGELOG.
* Parse comma-separated multiple values.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//975463004
* Upgrade to test 0.12.0.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1145403002
* Add support for separators.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1145413002
* Follow getopt in parsing option-like values.
The getopt function (and the GNU getopt_long extension) are the de facto
standards for option parsing, so we should follow their behavior to make
Dart programs consistent with other languages.
Closes dart-lang/args#36
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1165263003
* Fix the version number in the changelog.
I changed the pubspec but I forgot this.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1168923002.
* Use the new test runner on the bots.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1235363003 .
* Get rid of all the library tags.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1575233006 .
* Don't truncate multi-line command descriptions.
Closes dart-lang/args#42
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1603063004 .
* Fix 0.13.3+1 version in CHANGELOG
* Fix dart-lang/args#39 with docs change
* Add a CHANGELOG entry and bump the version.
* Document allowTrailingOptions in code
* Update the pubspec and changelog.
* replace deprecated getUsage() method
* Cut a release for the fix in b9049a8.
See dart-lang/args#46
* Make it strong mode clean.
R=nweiz@google.com
Review URL: https://codereview.chromium.org//1748333005 .
* README fixes
UsageError → UsageException
Fixed links to classes, methods etc. in command_runner library
* Release the changes in dart-lang/args#47.
* Add an explicit distinction between a command's description and summary.
6e6d9f282c364b321127be70d522be64d47cc902 broke some users who were
relying on only the first line of a command's description being included
in its runner's usage as a summary. That is once again the default
behavior.
To support the use case described in dart-lang/args#42, there's a new Command.summary
getter that explicitly controls the summary included in the parent
runner's usage. This defaults to the first line of the description, but
may be overridden to declare a multi-line summary.
Closes dart-lang/args#42
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1812923003 .
* Ensure that lists are reified.
This is important for strong mode, since "as List<String>" will fail for
a "List<dynamic>".
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1954923002 .
* UsageException isn't called UsageError.
Closes dart-lang/args#50.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//1985883002 .
* Allow command ArgParsers to be overridden.
This didn't work in strong mode, since they were declared as fields.
R=rnystrom@google.com
Review URL: https://codereview.chromium.org//2173623002 .
* Print the proper usage in CommandRunner. (dart-lang/args#53)
This now prints the usage for the command that failed to parse, rather
than always printing the root usage.
Closes dart-lang/args#52
* Fix an analysis hint. (dart-lang/args#56)
* Print command help for "bin --help command" (dart-lang/args#55)
Closes dart-lang/args#54
* Support command return value forwarding. (dart-lang/args#59)
This was unintentionally supported prior to 0.13.6+1. This re-adds
support, documents and tests it, and adds type annotations to make it
type-safe.
Closes dart-lang/args#57
Closes dart-lang/args#58
* Fix return value with latest change in pkg/test (dart-lang/args#63)
* Add Travic-CI support
Follow up on https://github.com/dart-lang/args/pull/64
* dartfmt
* Use the recommended name for analysis_options
And add/fix a number of standard lints
* Document that Command.argParser can be overridden. (dart-lang/args#72)
See dart-lang/args#68
* Add ArgParser.allowAnything(). (dart-lang/args#71)
Closes dart-lang/args#70
* Make allowTrailingOptions default to `false`. (dart-lang/args#66)
Make allowTrailingOptions default to `false`.
Also release 1.0.0.
* Update .travis.yml: allows failures in dartfmt dev (dart-lang/args#75)
* Remove pkg/test override
* Fix a fuzzy arrow warning (dart-lang/args#79)
Closes dart-lang/args#78
* Fix unused import warning in tests. (dart-lang/args#80)
* Take Iterables rather than Lists where possible (dart-lang/args#83)
Take Iterables rather than Lists where possible
Closes dart-lang/args#82
* Type the callback parameter to addOption as Function (dart-lang/args#84)
See flutter/flutterdart-lang/args#13223
* Type Command.run()'s return value as FutureOr<T> (dart-lang/args#87)
Closes dart-lang/args#85
* Improve the documentation option values
Closes dart-lang/args#73
* Deprecate upper-case constants in favor of lowercase
* Make Option's field names match addOption's parameters
* Reformat tests with the latest dartfmt
* Add a separate addMultiOption() method
This allows us to be more specific in our types and to keep flags that
are specific to multi-options in the place where they make sense.
Closes dart-lang/args#90
* Fix addMultiOption() and addOption(allowMultiple) usage (dart-lang/args#94)
We were printing the option list, with square brackets, even for
options that didn't have a default.
Fixes dart-lang/testdart-lang/args#774.
* Narrow the SDK constraint to require FutureOr (dart-lang/args#89)
Closes dart-lang/args#88
* Remove lints (dart-lang/args#98)
I don't believe in supporting lints that are generally unmaintained
and non-canonical.
* Display the default values for options with allowedHelp specified (dart-lang/args#100)
Closes dart-lang/args#99
* pubspec conventions dart-lang/site-wwwdart-lang/args#825
* Update .gitignore to new `dart_tool` pub cache
dart-lang/sdkdart-lang/args#32030
* Update README to use addMultiOption (dart-lang/args#104)
* chore: set max SDK version to <3.0.0 (dart-lang/args#106)
* Add optional wrapping of text for usage messages.
* Review changes
* Tweak the new word wrapping and get ready to release it.
- Renamed "maxLineLength" to "usageLineLength". In the context of
ArgParser, the former makes it sound like it's the argument line
itself that it applies to.
- Some style tweaks.
- Remove some ooooold workarounds. Strings support padRight() now.
- Run dartfmt on it.
- Bump the version number and update CHANGELOG.
* Document isWhitespace().
* Incorporate review feedback.
* Add support for wrapping command descriptions and other help text.
* Review changes.
* Bump version to publish.
* Enable and fix a number of standard lints and deprecated member use (dart-lang/args#111)
Improves the package health when analyzed on the pub site
* All deprecated usage from this package (dart-lang/args#112)
* Support usageLineLength in CommandRunner (dart-lang/args#115)
* dartfmt
* fix latest pedantic lints
* Fix deprecated URLs (dart-lang/args#117)
Remove superfluous codereview.settings
* Prepare to release 1.5.2 (dart-lang/args#118)
* Remove linter rules included in pedantic 1.8.0
* remove optional `new`s
* Fix newly enforced package:pedantic lints (dart-lang/args#126)
- always_declare_return_types
- annotate_overrides
- prefer_conditional_assignment
- prefer_if_null_operators
- prefer_single_quotes
- prefer_spread_collections
- unnecessary_this
- use_function_type_syntax_for_parameters
Bump min SDK to 2.3.0 to allow spreads in collection literals.
Drop unused author field from pubspec.
Restructure a string to avoid a line over 80 characters.
* Fix performance issues due to list.removeAt(0), which makes parsing O(n^2).
Add a test that checks that parse time does not increase more than 30x when input size grows from 10k to 100k. The current increase is about x2, so this is a very loose check that should be reliably green. It will only trigger if we hit O(n^2) behaviour again.
* Address review comments.
* address an issue when using ArgParser.allowAnything() with CommandRunner (dart-lang/args#132)
address an issue when using allowAnything
* With benchmark.
* Improve parse speed by switching from regexps to simple string operations.
50k short flags
before: 11ms, 11ms, 13ms, 11ms, 17ms
after: 6ms, 5ms, 6ms, 5ms, 8ms
50k abbreviations
before: 97ms, 114ms, 120ms, 111ms, 107ms
after: 39ms, 37ms, 51ms, 36ms, 36ms
50k long flags
before: 27ms, 26ms, 24ms, 24ms, 28ms
after: 15ms, 19ms, 19ms, 15ms, 15ms
50k long options with =
before: 27ms, 26ms, 27ms, 24ms, 26ms
after: 15ms, 19ms, 19ms, 15ms, 15ms
* prep for publishing 1.5.3 (dart-lang/args#133)
prep for publishing 1.5.3
* Address review comments.
* Update CHANGELOG.md
Co-Authored-By: Jacob MacDonald <jakemac@google.com>
* Enforce and fix comment_references lint (dart-lang/args#138)
* Reference CommandRunner from addCommand docs (dart-lang/args#139)
Closes dart-lang/args#134
The `addCommand` API is a little confusing, it doesn't impact the usage
string the way you might expect.
In the future we might consider hiding this API, for now call out the
confusing aspect and point to the API that most folks should use.
* Fix parsing: re-allow underscore in long names. (dart-lang/args#141)
* Changelog for underscore fix, prepare to publish (dart-lang/args#142)
* Remove extra blank lines between options (dart-lang/args#137)
Fixes dart-lang/args#48
The extra blank lines imply a grouping, however the grouping is
arbitrary based on which options happened to wrap, and may vary based on
the terminal width. Instead keep all options without extra newlines.
* Remove help form the list of commands in usage (dart-lang/args#140)
Closes dart-lang/args#131
The `help` command is redundant against the bottom line of the usage
text which explains how to use help in a more understandable way.
* Prepare for publish (dart-lang/args#143)
* Clean up some doc comments and code style (dart-lang/args#147)
- Note some argument restrictions when `name` must be a valid option.
- Remove a comment that adds no information on a constructor.
- Use a consistent pattern for checking valid option names -
`containsKey` rather than check for a `null` value.
- End a doc comment with a period.
- Change a getter doc comment to a noun phrase.
- Remove some repetitive details from doc comments, like types.
- Add explicit `show` to all exports rather than rely on catching all
package private stuff with a `hide`.
- Use `.toSet()` over `Set.from` to copy a set.
* Update http URLs to https (dart-lang/args#152)
Use https URLs to improve pub score.
* Remove unused dart:async imports. (dart-lang/args#157)
As of Dart 2.1, Future/Stream have been exported from dart:core.
* Improve the docs for CommandRunner (dart-lang/args#148)
* Migrate to null safety (dart-lang/args#159)
Initial migration to null safety, futher updates still pending and breaking changes possible.
* Remove already deprecated APIs (dart-lang/args#165)
* Use Null as return type for HelpCommand.run (dart-lang/args#164)
This may make it easier to understand choices around nullability by
making it explicit that the help command can never return a useful value
from it's `run` method.
Remove the unnecessary return value and return statement since a `Null`
return type may be treated similarly to `void`.
* Bump to 2.0.0 in anticipation of release. (dart-lang/args#166)
* Bump to 2.0.0 in anticipation of release.
Also tweaked a couple of tiny things.
* Correct version number.
* Remove ignore for deprecated usage (dart-lang/args#167)
* Give a specific function type for callback (dart-lang/args#168)
Closes dart-lang/args#149
* Fix link to use https (dart-lang/args#174)
* Re-expose the class OptionType (dart-lang/args#175)
This was hidden in dart-lang/args#147 but it is used by some external packages and
there is no strong reason to make this an additional breaking change.
* Refactor usage.dart (dart-lang/args#169)
- Add a top level method rather than require a constructor call and call
to `generate()` when there is otherwise no reason to hold on to the
`Usage` object. This makes it more clear that the implementation is
following a method object pattern.
- Update doc comment which had drifted from the original implementation
and no longer referred to the correct arguments.
- Make the `StringBuffer` field final since it is now a guaranteed
single use object.
- Make everything possible private. This revealed that there was a field
being updated but never read and can be removed.
- Remove `get` from method names.
- Make the `_columnWidths` late final rather than initialize it manually
in the call to `generate()`.
- Pull out sub-methods `_writeSeparator` and `_writeOption` from
`generate` to reduce nesting and the length of the overall method.
- use `.take(size)` instead of `.sublist(size)` since we only need an
Iterable.
- Use `.first` and `.last` over manual indexing.
- Use `.removeAt(0)` over `.removeRange(0,1)`.
- Rewrite a for loop with `.addAll` calls to a List literal with a
for loop element.
* Deprecate methods name get* (dart-lang/args#172)
Towards dart-lang/args#171
* Migrate to GitHub Actions (dart-lang/args#176)
* Add a test for passing allowedHelp without allowed (dart-lang/args#170)
Avoid a potential breaking solution for dart-lang/args#25
* stable null safety release (dart-lang/args#180)
* Add support for argument name aliases (dart-lang/args#182)
Closes https://github.com/dart-lang/args/issues/181
Add `aliases` named argument to `addFlag`, `addOption`, and `addMultiOption`, as well as a public `findByNameOrAlias` method on `ArgParser`. This allows you to provide aliases for an argument name, which eases the transition from one argument name to another.
Keys are _not_ added to the `options` map for each alias.
cc @Hixie
* Added mandatory (or required) option (dart-lang/args#177)
* Use --fatal-infos when analyzing on CI (dart-lang/args#184)
Fix some lints.
* Enable lint prefer_typing_uninitialized_variables (dart-lang/args#187)
Gives some safety against mistakes since there are fewer calls on a
statically dynamic variable.
* Split parse* methods to avoid duplicate work (dart-lang/args#185)
Each of the methods `parseSoloOption`, `parseAbbreviation`, and
`parseLongOption` do some string manipulation and checking, before
potentially calling the same method on their parent parser. No state
changes during parsing part. Split out a 'handle' method in each case
that takes a partially parsed result as arguments, and avoid duplicating
that work at each step in the parent chain. There may be other work that
could be avoided but it would take deeper refactoring. The
`parseShortFlag` does not need to be split since there are no
intermediate results computed from the String.
Make all possible members of `Parser` private.
Use a list literal for `_rest` in the initializer list instead of using `.addAll`
in the constructor body.
* Use latest setup action (dart-lang/args#188)
and test oldest supported SDK
* Update LICENSE (dart-lang/args#189)
Changes to comply with internal review
* publish 2.1.0 (dart-lang/args#191)
* clean up the sdk constraint and dep overrides (dart-lang/args#192)
* Fix newly enforced lint (dart-lang/args#193)
* fix a null assertion error when there are missing mandatory args in commands (dart-lang/args#199)
Fixes https://github.com/dart-lang/args/issues/197
The 2nd argument to ArgParserException is supposed to be the command chain that it was parsed from and not the option (note this is handled separately as well) - this was causing the command runner to throw a null assertion error later on by passing the name of the mandatory option as the command name, which doesn't exist as a command.
* Dart format with latest SDK (dart-lang/args#207)
* show similar commands with an edit distance of 2 or less (dart-lang/args#206)
Fixes dart-lang/args#201
Uses the Optimal string alignment distance algorithm to compute edit distance, which allows for additions, removals, substitutions, and swaps - all as 1 cost operations.
Suggests any commands within an edit distance of <=2 by default, which is configurable. You can set it to 0 to disable the feature.
* Add ability to group commands by category in usage (dart-lang/args#202)
To enable https://github.com/flutter/flutter/issues/83706
Formatting is loosely based on what `Brew` does, and open to suggestions. Existing tests are unchanged.
Example (all displayed commands categorized):
<img width="669" alt="Screenshot 2021-07-23 at 16 22 40" src="https://user-images.githubusercontent.com/6655696/126796093-e8652385-c3d7-4600-83b1-76852ab46ea4.png">
* Move from pedantic to lints package (dart-lang/args#208)
Fix existing violations of lints:
- `prefer_initializing_formals`
- `unnecessary_string_interpolations`
- `prefer_function_declarations_over_variables`
Ignore one case of `prefer_void_to_null` to work around
https://github.com/dart-lang/linter/issues/2792
* Fix pre-existing HintCode.UNNECESSARY_TYPE_CHECK_TRUE (dart-lang/args#209)
This prepares for landing https://dart-review.googlesource.com/c/sdk/+/190360 into the analyzer.
* Update documentation (dart-lang/args#211)
In order to add a global argument to `CommandRunner`, you need to add it through its `argParser` (reference to dart-lang/args#203)
* Renamed to conform to pub.dev standards (dart-lang/args#213)
* Update pubspec.yaml (dart-lang/args#215)
* prep to publish 2.3.1 (dart-lang/args#218)
* Add a CommandRunner example (dart-lang/args#220)
* Add a command runner example
* Update readme
* Review comments
* Require Dart 2.18, update lints, update CI, add dependabot (dart-lang/args#222)
* blast_repo fixes (dart-lang/args#226)
Dependabot
* Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/args#229)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8...755da8c3cf115ac066823e79a1e1788f8940201b)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* blast_repo fixes (dart-lang/args#230)
* blast_repo fixes
auto-publish
* publish from master
* update the publishing script (dart-lang/args#232)
* Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/args#233)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/755da8c3cf115ac066823e79a1e1788f8940201b...ac593985615ec2ede58e132d2e21d2b1cbd6127c)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/args#234)
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.3 to 1.4.
- [Release notes](https://github.com/dart-lang/setup-dart/releases)
- [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dart-lang/setup-dart/compare/6a218f2413a3e78e9087f638a238f6b40893203d...a57a6c04cf7d4840e88432aad6281d1e125f0d46)
---
updated-dependencies:
- dependency-name: dart-lang/setup-dart
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Suggest command based on aliases (dart-lang/args#235)
* Update README.md (dart-lang/args#236)
The `allowMultiple` option of `addOption` was no longer available, so this change will correct it to `addMultiOption`.
* update readme; add contributing doc (dart-lang/args#237)
* update readme; add contributing doc
* add the readme
* Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/args#239)
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/dart-lang/setup-dart/releases)
- [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dart-lang/setup-dart/compare/a57a6c04cf7d4840e88432aad6281d1e125f0d46...d6a63dab3335f427404425de0fbfed4686d93c4f)
---
updated-dependencies:
- dependency-name: dart-lang/setup-dart
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/args#238)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.5.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/ac593985615ec2ede58e132d2e21d2b1cbd6127c...8f4b7f84864484a7bf31766abe9204da3cbe65b3)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/args#241)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/8f4b7f84864484a7bf31766abe9204da3cbe65b3...8e5e7e5ab8b370d6c329ec480221332ada57f0ab)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* added package topics to the pubspec file (dart-lang/args#242)
* blast_repo fixes (dart-lang/args#243)
dependabot
* adjust the validation of mandatory options (dart-lang/args#246)
* adjust the validation of mandatory options
* typo
* switch to using package:dart_flutter_team_lints (dart-lang/args#247)
* switch to using package:dart_flutter_team_lints
* address example lints
* require dart 2.19
* update action config
* Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/args#249)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v3.5.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix: Checkout Issue in self hosted runner due to faulty submodule check-ins by <a href="https://github.com/megamanics"><code>@âmegamanics</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1196">actions/checkoutdart-lang/args#1196</a></li>
<li>Fix typos found by codespell by <a href="https://github.com/DimitriPapadopoulos"><code>@âDimitriPapadopoulos</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1287">actions/checkoutdart-lang/args#1287</a></li>
<li>Add support for sparse checkouts by <a href="https://github.com/dscho"><code>@âdscho</code></a> and <a href="https://github.com/dfdez"><code>@âdfdez</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1369">actions/checkoutdart-lang/args#1369</a></li>
<li>Release v3.5.3 by <a href="https://github.com/TingluoHuang"><code>@âTingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1376">actions/checkoutdart-lang/args#1376</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/megamanics"><code>@âmegamanics</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1196">actions/checkoutdart-lang/args#1196</a></li>
<li><a href="https://github.com/DimitriPapadopoulos"><code>@âDimitriPapadopoulos</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1287">actions/checkoutdart-lang/args#1287</a></li>
<li><a href="https://github.com/dfdez"><code>@âdfdez</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1369">actions/checkoutdart-lang/args#1369</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3...v3.5.3">https://github.com/actions/checkout/compare/v3...v3.5.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@âactions/io</code></a></li>
</ul>
<h2>v3.3.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1045">Implement branch list using callbacks from exec function</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1050">Add in explicit reference to private checkout options</a></li>
<li>[Fix comment typos (that got added in <a href="https://redirect.github.com/actions/checkout/issues/770">dart-lang/args#770</a>)](<a href="https://redirect.github.com/actions/checkout/pull/1057">actions/checkoutdart-lang/args#1057</a>)</li>
</ul>
<h2>v3.2.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/942">Add GitHub Action to perform release</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/967">Fix status badge</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1002">Replace datadog/squid with ubuntu/squid Docker image</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/964">Wrap pipeline commands for submoduleForeach in quotes</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1029">Update <code>@âactions/io</code> to 1.1.2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1039">Upgrading version to 3.2.0</a></li>
</ul>
<h2>v3.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/939">Use <code>@âactions/core</code> <code>saveState</code> and <code>getState</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/922">Add <code>github-server-url</code> input</a></li>
</ul>
<h2>v3.0.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/770">Add input <code>set-safe-directory</code></a></li>
</ul>
<h2>v3.0.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/762">Fixed an issue where checkout failed to run in container jobs due to the new git setting <code>safe.directory</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/744">Bumped various npm package versions</a></li>
</ul>
<h2>v3.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/689">Update to node 16</a></li>
</ul>
<h2>v2.3.1</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/c85c95e3d7251135ab7dc9ce3241c5835cc595a9"><code>c85c95e</code></a> Release v3.5.3 (<a href="https://redirect.github.com/actions/checkout/issues/1376">dart-lang/args#1376</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/d106d4669b3bfcb17f11f83f98e1cab478e9f635"><code>d106d46</code></a> Add support for sparse checkouts (<a href="https://redirect.github.com/actions/checkout/issues/1369">dart-lang/args#1369</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/f095bcc56b7c2baf48f3ac70d6d6782f4f553222"><code>f095bcc</code></a> Fix typos found by codespell (<a href="https://redirect.github.com/actions/checkout/issues/1287">dart-lang/args#1287</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/47fbe2df0ad0e27efb67a70beac3555f192b062f"><code>47fbe2d</code></a> Fix: Checkout fail in self-hosted runners when faulty submodule are checked-i...</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/8e5e7e5ab8b370d6c329ec480221332ada57f0ab...c85c95e3d7251135ab7dc9ce3241c5835cc595a9">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/args#254)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v3.6.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Mark test scripts with Bash'isms to be run via Bash by <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1377">actions/checkoutdart-lang/args#1377</a></li>
<li>Add option to fetch tags even if fetch-depth > 0 by <a href="https://github.com/RobertWieczoreck"><code>@âRobertWieczoreck</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/579">actions/checkoutdart-lang/args#579</a></li>
<li>Release 3.6.0 by <a href="https://github.com/luketomlinson"><code>@âluketomlinson</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1437">actions/checkoutdart-lang/args#1437</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/RobertWieczoreck"><code>@âRobertWieczoreck</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/579">actions/checkoutdart-lang/args#579</a></li>
<li><a href="https://github.com/luketomlinson"><code>@âluketomlinson</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1437">actions/checkoutdart-lang/args#1437</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3.5.3...v3.6.0">https://github.com/actions/checkout/compare/v3.5.3...v3.6.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@âactions/io</code></a></li>
</ul>
<h2>v3.3.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1045">Implement branch list using callbacks from exec function</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1050">Add in explicit reference to private checkout options</a></li>
<li>[Fix comment typos (that got added in <a href="https://redirect.github.com/actions/checkout/issues/770">dart-lang/args#770</a>)](<a href="https://redirect.github.com/actions/checkout/pull/1057">actions/checkoutdart-lang/args#1057</a>)</li>
</ul>
<h2>v3.2.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/942">Add GitHub Action to perform release</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/967">Fix status badge</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1002">Replace datadog/squid with ubuntu/squid Docker image</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/964">Wrap pipeline commands for submoduleForeach in quotes</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1029">Update <code>@âactions/io</code> to 1.1.2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1039">Upgrading version to 3.2.0</a></li>
</ul>
<h2>v3.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/939">Use <code>@âactions/core</code> <code>saveState</code> and <code>getState</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/922">Add <code>github-server-url</code> input</a></li>
</ul>
<h2>v3.0.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/770">Add input <code>set-safe-directory</code></a></li>
</ul>
<h2>v3.0.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/762">Fixed an issue where checkout failed to run in container jobs due to the new git setting <code>safe.directory</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/744">Bumped various npm package versions</a></li>
</ul>
<h2>v3.0.0</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/f43a0e5ff2bd294095638e18286ca9a3d1956744"><code>f43a0e5</code></a> Release 3.6.0 (<a href="https://redirect.github.com/actions/checkout/issues/1437">dart-lang/args#1437</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/7739b9ba2efcda9dde65ad1e3c2dbe65b41dfba7"><code>7739b9b</code></a> Add option to fetch tags even if fetch-depth > 0 (<a href="https://redirect.github.com/actions/checkout/issues/579">dart-lang/args#579</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/96f53100ba2a5449eb71d2e6604bbcd94b9449b5"><code>96f5310</code></a> Mark test scripts with Bash'isms to be run via Bash (<a href="https://redirect.github.com/actions/checkout/issues/1377">dart-lang/args#1377</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/c85c95e3d7251135ab7dc9ce3241c5835cc595a9...f43a0e5ff2bd294095638e18286ca9a3d1956744">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/args#257)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README.md for V4 by <a href="https://github.com/sivapalan"><code>@âsivapalan</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1452">actions/checkoutdart-lang/args#1452</a></li>
<li>Add support for partial checkout filters by <a href="https://github.com/finleygn"><code>@âfinleygn</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1396">actions/checkoutdart-lang/args#1396</a></li>
<li>Prepare 4.1.0 release by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1496">actions/checkoutdart-lang/args#1496</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/sivapalan"><code>@âsivapalan</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1452">actions/checkoutdart-lang/args#1452</a></li>
<li><a href="https://github.com/finleygn"><code>@âfinleygn</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1396">actions/checkoutdart-lang/args#1396</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.0.0...v4.1.0">https://github.com/actions/checkout/compare/v4.0.0...v4.1.0</a></p>
<h2>v4.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update default runtime to node20 by <a href="https://github.com/takost"><code>@âtakost</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1436">actions/checkoutdart-lang/args#1436</a></li>
<li>Support fetching without the --progress option by <a href="https://github.com/simonbaird"><code>@âsimonbaird</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1067">actions/checkoutdart-lang/args#1067</a></li>
<li>Release 4.0.0 by <a href="https://github.com/takost"><code>@âtakost</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1447">actions/checkoutdart-lang/args#1447</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/takost"><code>@âtakost</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1436">actions/checkoutdart-lang/args#1436</a></li>
<li><a href="https://github.com/simonbaird"><code>@âsimonbaird</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1067">actions/checkoutdart-lang/args#1067</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3...v4.0.0">https://github.com/actions/checkout/compare/v3...v4.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@âactions/io</code></a></li>
</ul>
<h2>v3.3.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1045">Implement branch list using callbacks from exec function</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1050">Add in explicit reference to private checkout options</a></li>
<li>[Fix comment typos (that got added in <a href="https://redirect.github.com/actions/checkout/issues/770">dart-lang/args#770</a>)](<a href="https://redirect.github.com/actions/checkout/pull/1057">actions/checkoutdart-lang/args#1057</a>)</li>
</ul>
<h2>v3.2.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/942">Add GitHub Action to perform release</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/967">Fix status badge</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1002">Replace datadog/squid with ubuntu/squid Docker image</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/964">Wrap pipeline commands for submoduleForeach in quotes</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1029">Update <code>@âactions/io</code> to 1.1.2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1039">Upgrading version to 3.2.0</a></li>
</ul>
<h2>v3.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/939">Use <code>@âactions/core</code> <code>saveState</code> and <code>getState</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/922">Add <code>github-server-url</code> input</a></li>
</ul>
<h2>v3.0.2</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/8ade135a41bc03ea155e62e844d188df1ea18608"><code>8ade135</code></a> Prepare 4.1.0 release (<a href="https://redirect.github.com/actions/checkout/issues/1496">dart-lang/args#1496</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/c533a0a4cfc4962971818edcfac47a2899e69799"><code>c533a0a</code></a> Add support for partial checkout filters (<a href="https://redirect.github.com/actions/checkout/issues/1396">dart-lang/args#1396</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/72f2cec99f417b1a1c5e2e88945068983b7965f9"><code>72f2cec</code></a> Update README.md for V4 (<a href="https://redirect.github.com/actions/checkout/issues/1452">dart-lang/args#1452</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/3df4ab11eba7bda6032a0b82a6bb43b11571feac"><code>3df4ab1</code></a> Release 4.0.0 (<a href="https://redirect.github.com/actions/checkout/issues/1447">dart-lang/args#1447</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/8b5e8b768746b50394015010d25e690bfab9dfbc"><code>8b5e8b7</code></a> Support fetching without the --progress option (<a href="https://redirect.github.com/actions/checkout/issues/1067">dart-lang/args#1067</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/97a652b80035363df47baee5031ec8670b8878ac"><code>97a652b</code></a> Update default runtime to node20 (<a href="https://redirect.github.com/actions/checkout/issues/1436">dart-lang/args#1436</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/f43a0e5ff2bd294095638e18286ca9a3d1956744...8ade135a41bc03ea155e62e844d188df1ea18608">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/args#256)
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.0 to 1.5.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/releases">dart-lang/setup-dart's releases</a>.</em></p>
<blockquote>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types (the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with forward compatibility that switches when the rename happens.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md">dart-lang/setup-dart's changelog</a>.</em></p>
<blockquote>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just
the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types
(the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with
forward compatibility that switches when the rename happens.</li>
</ul>
<h2>v1.5.0</h2>
<ul>
<li>Re-wrote the implementation of the action into Dart.</li>
<li>Auto-detect the platform architecture (<code>x64</code>, <code>ia32</code>, <code>arm</code>, <code>arm64</code>).</li>
<li>Improved the caching and download resilience of the sdk.</li>
<li>Added a new action output: <code>dart-version</code> - the installed version of the sdk.</li>
</ul>
<h2>v1.4.0</h2>
<ul>
<li>Automatically create OIDC token for pub.dev.</li>
<li>Add a reusable workflow for publishing.</li>
</ul>
<h2>v1.3.0</h2>
<ul>
<li>The install location of the Dart SDK is now available
in an environment variable, <code>DART_HOME</code>
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/43">dart-lang/args#43</a>).</li>
<li>Fixed an issue where cached downloads could lead to unzip issues
on self-hosted runners
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/35">dart-lang/args#35</a>).</li>
</ul>
<h2>v1.2.0</h2>
<ul>
<li>Fixed a path issue impacting git dependencies on Windows.</li>
</ul>
<h2>v1.1.0</h2>
<ul>
<li>Added a <code>flavor</code> option setup.sh to allow downloading unpublished builds.</li>
</ul>
<h2>v1.0.0</h2>
<ul>
<li>Promoted to 1.0 stable.</li>
</ul>
<h2>v0.5</h2>
<ul>
<li>Fixed a Windows <code>pub global activate</code> path issue.</li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dart-lang/setup-dart/commit/8a4b97ea2017cc079571daec46542f76189836b1"><code>8a4b97e</code></a> Support renaming the be channel to main. (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/102">dart-lang/args#102</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/0970dcf46e056fb521e7027775fdff076fe1e732"><code>0970dcf</code></a> Bump <code>@âactions/http-client</code> from 2.1.0 to 2.1.1 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/101">dart-lang/args#101</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/e58aeb62aef51dcc4d0ba8eada7c08092aad5314"><code>e58aeb6</code></a> updates for the latest version of extension types (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/100">dart-lang/args#100</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/deafe40ee7284c48e67c783c88eed8c8b52b19ca"><code>deafe40</code></a> Convert to extension types (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/99">dart-lang/args#99</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/cdb51fff2b088939ef86fe041b18e82cb4733522"><code>cdb51ff</code></a> Bump semver from 6.3.0 to 6.3.1 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/98">dart-lang/args#98</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/e2fce1213c49504e0029ade4bac39403dfc11fb3"><code>e2fce12</code></a> update JS interop - remove JS typedef references (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/97">dart-lang/args#97</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/42c988f214b6652e0b56ca2ad2f1d477bf6098b7"><code>42c988f</code></a> set up a cron to build the action's javascript (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/96">dart-lang/args#96</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/007c7cbb377cdbdf8922e2f8c4172dc60fbaaf9a"><code>007c7cb</code></a> blast_repo fixes (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/92">dart-lang/args#92</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/08de7e0c9a57cb3229b052af11c7f8eae4a845bd"><code>08de7e0</code></a> Remove annotations no longer needed (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/91">dart-lang/args#91</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/bd8bef0960777f45de11f484bcb7beacadb321cf"><code>bd8bef0</code></a> Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/89">dart-lang/args#89</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/dart-lang/setup-dart/compare/d6a63dab3335f427404425de0fbfed4686d93c4f...8a4b97ea2017cc079571daec46542f76189836b1">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/args#259)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Update CODEOWNERS to Launch team by <a href="https://github.com/joshmgross"><code>@âjoshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkoutdart-lang/args#1510</a></li>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/joshmgross"><code>@âjoshmgross</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1510">actions/checkoutdart-lang/args#1510</a></li>
<li><a href="https://github.com/peterbe"><code>@âpeterbe</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.0...v4.1.1">https://github.com/actions/checkout/compare/v4.1.0...v4.1.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/b4ffde65f46336ab88eb53be808477a3936bae11"><code>b4ffde6</code></a> Link to release page from what's new section (<a href="https://redirect.github.com/actions/checkout/issues/1514">dart-lang/args#1514</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/8530928916aaef40f59e6f221989ccb31f5759e7"><code>8530928</code></a> Correct link to GitHub Docs (<a href="https://redirect.github.com/actions/checkout/issues/1511">dart-lang/args#1511</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/7cdaf2fbc075e6f3b9ca94cfd6cec5adc8a75622"><code>7cdaf2f</code></a> Update CODEOWNERS to Launch team (<a href="https://redirect.github.com/actions/checkout/issues/1510">dart-lang/args#1510</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/8ade135a41bc03ea155e62e844d188df1ea18608...b4ffde65f46336ab88eb53be808477a3936bae11">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/args#258)
â ï¸ **Dependabot is rebasing this PR** â ï¸
Rebasing might not happen immediately, so don't worry if this takes some time.
Note: if you make any changes to this PR yourself, they will take precedence over the rebase.
---
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.1 to 1.6.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/releases">dart-lang/setup-dart's releases</a>.</em></p>
<blockquote>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md">dart-lang/setup-dart's changelog</a>.</em></p>
<blockquote>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just
the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types
(the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with
forward compatibility that switches when the rename happens.</li>
</ul>
<h2>v1.5.0</h2>
<ul>
<li>Re-wrote the implementation of the action into Dart.</li>
<li>Auto-detect the platform architecture (<code>x64</code>, <code>ia32</code>, <code>arm</code>, <code>arm64</code>).</li>
<li>Improved the caching and download resilience of the sdk.</li>
<li>Added a new action output: <code>dart-version</code> - the installed version of the sdk.</li>
</ul>
<h2>v1.4.0</h2>
<ul>
<li>Automatically create OIDC token for pub.dev.</li>
<li>Add a reusable workflow for publishing.</li>
</ul>
<h2>v1.3.0</h2>
<ul>
<li>The install location of the Dart SDK is now available
in an environment variable, <code>DART_HOME</code>
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/43">dart-lang/args#43</a>).</li>
<li>Fixed an issue where cached downloads could lead to unzip issues
on self-hosted runners
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/35">dart-lang/args#35</a>).</li>
</ul>
<h2>v1.2.0</h2>
<ul>
<li>Fixed a path issue impacting git dependencies on Windows.</li>
</ul>
<h2>v1.1.0</h2>
<ul>
<li>Added a <code>flavor</code> option setup.sh to allow downloading unpublished builds.</li>
</ul>
<h2>v1.0.0</h2>
<ul>
<li>Promoted to 1.0 stable.</li>
</ul>
<h2>v0.5</h2>
<ul>
<li>Fixed a Windows <code>pub global activate</code> path issue.</li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dart-lang/setup-dart/commit/b64355ae6ca0b5d484f0106a033dd1388965d06d"><code>b64355a</code></a> debugging issues around specifying the latest patch release (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/108">dart-lang/args#108</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/cd21d0e7475915575e022d2cacd7c54e5026e159"><code>cd21d0e</code></a> Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/110">dart-lang/args#110</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/2118353f672662accb7785f6afd28ab61c7cfec9"><code>2118353</code></a> Bump actions/checkout from 3 to 4 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/109">dart-lang/args#109</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/8ef13bcca6c9bb2219391088bc9d968125f68406"><code>8ef13bc</code></a> support sdk version wildcard format (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/106">dart-lang/args#106</a>)</li>
<li>See full diff in <a href="https://github.com/dart-lang/setup-dart/compare/8a4b97ea2017cc079571daec46542f76189836b1...b64355ae6ca0b5d484f0106a033dd1388965d06d">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Add missing curly braces in README.md (dart-lang/args#261)
* Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (dart-lang/args#263)
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.0 to 1.6.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/releases">dart-lang/setup-dart's releases</a>.</em></p>
<blockquote>
<h2>v1.6.2</h2>
<ul>
<li>Switch to running the workflow on <code>node20</code> from <code>node16</code>. See also <a href="https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/">Transitioning from Node 16 to Node 20</a>.</li>
</ul>
<h2>v1.6.1</h2>
<ul>
<li>Updated the google storage url for <code>main</code> channel releases.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md">dart-lang/setup-dart's changelog</a>.</em></p>
<blockquote>
<h2>v1.6.2</h2>
<ul>
<li>Switch to running the workflow on <code>node20`` from </code>node16`. See also
<a href="https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/">Transitioning from Node 16 to Node 20</a>.</li>
</ul>
<h2>v1.6.1</h2>
<ul>
<li>Updated the google storage url for <code>main</code> channel releases.</li>
</ul>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just
the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types
(the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with
forward compatibility that switches when the rename happens.</li>
</ul>
<h2>v1.5.0</h2>
<ul>
<li>Re-wrote the implementation of the action into Dart.</li>
<li>Auto-detect the platform architecture (<code>x64</code>, <code>ia32</code>, <code>arm</code>, <code>arm64</code>).</li>
<li>Improved the caching and download resilience of the sdk.</li>
<li>Added a new action output: <code>dart-version</code> - the installed version of the sdk.</li>
</ul>
<h2>v1.4.0</h2>
<ul>
<li>Automatically create OIDC token for pub.dev.</li>
<li>Add a reusable workflow for publishing.</li>
</ul>
<h2>v1.3.0</h2>
<ul>
<li>The install location of the Dart SDK is now available
in an environment variable, <code>DART_HOME</code>
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/43">dart-lang/args#43</a>).</li>
<li>Fixed an issue where cached downloads could lead to unzip issues
on self-hosted runners
(<a href="https://redirect.github.com/dart-lang/setup-dart/issues/35">dart-lang/args#35</a>).</li>
</ul>
<h2>v1.2.0</h2>
<ul>
<li>Fixed a path issue impacting git dependencies on Windows.</li>
</ul>
<h2>v1.1.0</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dart-lang/setup-dart/commit/fedb1266e91cf51be2fdb382869461a434b920a3"><code>fedb126</code></a> switch to using node20 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/122">dart-lang/args#122</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/ca7e6fee45ffbd82b555a7ebfc236d2c86439f5b"><code>ca7e6fe</code></a> update the changelog; prep to release 1.6.1 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/120">dart-lang/args#120</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/c1b2cdbfafc77480d10fe0246ef4dd2f83a9e7b7"><code>c1b2cdb</code></a> Clean up after renaming the be channel to main. (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/115">dart-lang/args#115</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/49b0b8e0a88f72a8fdf1319a41cc261cec63c3c7"><code>49b0b8e</code></a> Bump actions/checkout from 3 to 4 in README.md (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/117">dart-lang/args#117</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/7f54cd0cee53e120db0d1fce4196b7772ebd6f6e"><code>7f54cd0</code></a> Bump <code>@âactions/http-client</code> from 2.1.1 to 2.2.0 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/112">dart-lang/args#112</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/6e2fe379bd3c8a39facc503f4494396e0de36f13"><code>6e2fe37</code></a> Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/113">dart-lang/args#113</a>)</li>
<li>See full diff in <a href="https://github.com/dart-lang/setup-dart/compare/b64355ae6ca0b5d484f0106a033dd1388965d06d...fedb1266e91cf51be2fdb382869461a434b920a3">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Introduce new typed `ArgResults` `flag(String)`, `option(String)`, and `multiOption(String)` methods (dart-lang/args#248)
* introduce types methods to ArgResults
* rename methods
* updates
* review feedback
* reduce PR diffs
* reduce PR diffs
* review feedback
* Update arg_results.dart
* Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/args#266)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.2</h2>
<p>We are investigating the following issue with this release and have rolled-back the <code>v4</code> tag to point to <code>v4.1.1</code></p>
<ul>
<li><code>sparse-checkout</code> is not available on git versions prior to 2.27.0 (see <a href="https://redirect.github.com/actions/checkout/issues/1651">actions/checkoutdart-lang/args#1651</a>)</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
<li>Bump tough-cookie from 4.0.0 to 4.1.3 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1406">actions/checkoutdart-lang/args#1406</a></li>
<li>Bump <code>@âbabel/traverse</code> from 7.20.5 to 7.24.0 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1642">actions/checkoutdart-lang/args#1642</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jww3"><code>@âjww3</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1616">actions/checkoutdart-lang/args#1616</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.1...v4.1.2">https://github.com/actions/checkout/compare/v4.1.1...v4.1.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.2</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
</ul>
<h2>v4.1.1</h2>
<ul>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@âactions/io</code></a></li>
</ul>
<h2>v3.3.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1045">Implement branch list using callbacks from exec function</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1050">Add in explicit reference to private checkout options</a></li>
<li>[Fix comment typos (that got added in <a href="https://redirect.github.com/actions/checkout/issues/770">dart-lang/args#770</a>)](<a href="https://redirect.github.com/actions/checkout/pull/1057">actions/checkoutdart-lang/args#1057</a>)</li>
</ul>
<h2>v3.2.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/942">Add GitHub Action to perform release</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/967">Fix status badge</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1002">Replace datadog/squid with ubuntu/squid Docker image</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/964">Wrap pipeline commands for submoduleForeach in quotes</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1029">Update <code>@âactions/io</code> to 1.1.2</a></li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/9bb56186c3b09b4f86b1c65136769dd318469633"><code>9bb5618</code></a> Prep for release of v4.1.2 (<a href="https://redirect.github.com/actions/checkout/issues/1649">dart-lang/args#1649</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/8eb1f6a495037164bea451156472f35fdd6bafc0"><code>8eb1f6a</code></a> Bump <code>@âbabel/traverse</code> from 7.20.5 to 7.24.0 (<a href="https://redirect.github.com/actions/checkout/issues/1642">dart-lang/args#1642</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/556e4c3cb0b8b54b734286d5439adadcb0a8cb92"><code>556e4c3</code></a> Bump tough-cookie from 4.0.0 to 4.1.3 (<a href="https://redirect.github.com/actions/checkout/issues/1406">dart-lang/args#1406</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/b32f140b0c872d58512e0a66172253c302617b90"><code>b32f140</code></a> Warn on attempts to publish <code>test-ubuntu-git</code> from non-main branch. (<a href="https://redirect.github.com/actions/checkout/issues/1623">dart-lang/args#1623</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/2650dbd060003e3b5ae211e4358852f336b682a7"><code>2650dbd</code></a> Give <code>test-ubuntu-git</code> its own <code>README</code> (<a href="https://redirect.github.com/actions/checkout/issues/1620">dart-lang/args#1620</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/aadec899646c8e0f34c52d9219c2faac36626b55"><code>aadec89</code></a> Explicitly disable sparse checkout unless asked for (<a href="https://redirect.github.com/actions/checkout/issues/1598">dart-lang/args#1598</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/df0bcddf6d6823307c716b56a7ef9c3b25078874"><code>df0bcdd</code></a> Refine workflow for generating <code>test-ubuntu-git</code> (<a href="https://redirect.github.com/actions/checkout/issues/1617">dart-lang/args#1617</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/473055ba18d6d2da209cd46110aadb9275e3194e"><code>473055b</code></a> Create <code>test-ubuntu-git</code> Docker Container for Proxy Tests (<a href="https://redirect.github.com/actions/checkout/issues/1616">dart-lang/args#1616</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/b4ffde65f46336ab88eb53be808477a3936bae11...9bb56186c3b09b4f86b1c65136769dd318469633">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* rev to 2.5.0 and prep to publish (dart-lang/args#267)
* Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/args#268)
Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.2 to 1.6.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/releases">dart-lang/setup-dart's releases</a>.</em></p>
<blockquote>
<h2>v1.6.4</h2>
<ul>
<li>Rebuild JS code to include changes from v1.6.3</li>
</ul>
<h2>v1.6.3</h2>
<ul>
<li>Roll <code>undici</code> dependency to address <a href="https://github.com/nodejs/undici/security/advisories/GHSA-m4v8-wqvr-p9f7">CVE-2024-30260</a> and <a href="https://github.com/nodejs/undici/security/advisories/GHSA-9qxr-qj54-h672">CVE-2024-30261</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md">dart-lang/setup-dart's changelog</a>.</em></p>
<blockquote>
<h2>v1.6.4</h2>
<ul>
<li>Rebuild JS code.</li>
</ul>
<h2>v1.6.3</h2>
<ul>
<li>Roll <code>undici</code> dependency to address <a href="https://github.com/nodejs/undici/security/advisories/GHSA-m4v8-wqvr-p9f7">CVE-2024-30260</a> and <a href="https://github.com/nodejs/undici/security/advisories/GHSA-9qxr-qj54-h672">CVE-2024-30261</a>.</li>
</ul>
<h2>v1.6.2</h2>
<ul>
<li>Switch to running the workflow on <code>node20`` from </code>node16`. See also
<a href="https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/">Transitioning from Node 16 to Node 20</a>.</li>
</ul>
<h2>v1.6.1</h2>
<ul>
<li>Updated the google storage url for <code>main</code> channel releases.</li>
</ul>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just
the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types
(the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with
forward compatibility that switches when the rename happens.</li>
</ul>
<h2>v1.5.0</h2>
<ul>
<li>Re-wrote the implementation of the action into Dart.</li>
<li>Auto-detect the platform architecture (<code>x64</code>, <code>ia32</code>, <code>arm</code>, <code>arm64</code>).</li>
<li>Improved the caching and download resilience of the sdk.</li>
<li>Added a new action output: <code>dart-version</code> - the installed version of the sdk.</li>
</ul>
<h2>v1.4.0</h2>
<ul>
<li>Automatically create OIDC token for pub.dev.</li>
<li>Add a reusable workflow for publishing.</li>
</ul>
<h2>v1.3.0</h2>
<ul>
<li>The install location of the Dart SDK is now available</li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dart-lang/setup-dart/commit/f0ead981b4d9a35b37f30d36160575d60931ec30"><code>f0ead98</code></a> Rebuild JS code (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/129">dart-lang/args#129</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/65c82982aa686933bf10d50aced7a27b2b63f2a6"><code>65c8298</code></a> Update CHANGELOG.md (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/128">dart-lang/args#128</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/57338d64c065775c2cf86eaf665316ea44e0249d"><code>57338d6</code></a> Bump undici from 5.28.3 to 5.28.4 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/127">dart-lang/args#127</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/05d3f5ec28dca4e3ae8991f2be7828a62942f8a8"><code>05d3f5e</code></a> Bump <code>@âactions/http-client</code> from 2.2.0 to 2.2.1 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/126">dart-lang/args#126</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/b8c0b77d1f6fb52c5ddff2fc3da6cfa3f9585860"><code>b8c0b77</code></a> no longer specify the inline-class experiment flag (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/125">dart-lang/args#125</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/b9952d818df9b43bab696de0e1b7bbf09464a829"><code>b9952d8</code></a> Bump undici from 5.27.0 to 5.28.3 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/124">dart-lang/args#124</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/9a6ee8904aa6a1fa76b02bb3e7b0ec30cddcaaac"><code>9a6ee89</code></a> Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/123">dart-lang/args#123</a>)</li>
<li>See full diff in <a href="https://github.com/dart-lang/setup-dart/compare/fedb1266e91cf51be2fdb382869461a434b920a3...f0ead981b4d9a35b37f30d36160575d60931ec30">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* Update to latest lints, require Dart 3.3 (dart-lang/args#270)
* Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/args#271)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.4</h2>
<h2>What's Changed</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkoutdart-lang/args#1692</a></li>
<li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkoutdart-lang/args#1688</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkoutdart-lang/args#1643</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkoutdart-lang/args#1693</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.3...v4.1.4">https://github.com/actions/checkout/compare/v4.1.3...v4.1.4</a></p>
<h2>v4.1.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1650">actions/checkoutdart-lang/args#1650</a></li>
<li>Check git version before attempting to disable <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1656">actions/checkoutdart-lang/args#1656</a></li>
<li>Add SSH user parameter by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1685">actions/checkoutdart-lang/args#1685</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.2...v4.1.3">https://github.com/actions/checkout/compare/v4.1.2...v4.1.3</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkoutdart-lang/args#1692</a></li>
<li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkoutdart-lang/args#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkoutdart-lang/args#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkoutdart-lang/args#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<ul>
<li>Check git version before attempting to disable <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1656">actions/checkoutdart-lang/args#1656</a></li>
<li>Add SSH user parameter by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1685">actions/checkoutdart-lang/args#1685</a></li>
<li>Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1650">actions/checkoutdart-lang/args#1650</a></li>
</ul>
<h2>v4.1.2</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
</ul>
<h2>v4.1.1</h2>
<ul>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@âactions/io</code></a></li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/0ad4b8fadaa221de15dcec353f45205ec38ea70b"><code>0ad4b8f</code></a> Prep Release v4.1.4 (<a href="https://redirect.github.com/actions/checkout/issues/1704">dart-lang/args#1704</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/43045ae669be728bd34ed56fcd1a230c0dc4d8e2"><code>43045ae</code></a> Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> (<a href="https://redirect.github.com/actions/checkout/issues/1692">dart-lang/args#1692</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/37b082107ba410260a3aaddf93122e04801ce631"><code>37b0821</code></a> Bump the minor-actions-dependencies group with 2 updates (<a href="https://redirect.github.com/actions/checkout/issues/1693">dart-lang/args#1693</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/9839dc14a02ddc6b6995e69eb3ecb98132fc8b6b"><code>9839dc1</code></a> Add dependabot config (<a href="https://redirect.github.com/actions/checkout/issues/1688">dart-lang/args#1688</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/9b4c13b0bfa31b4514c14f74b5a166c2708f43c6"><code>9b4c13b</code></a> Bump word-wrap from 1.2.3 to 1.2.5 (<a href="https://redirect.github.com/actions/checkout/issues/1643">dart-lang/args#1643</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/1d96c772d19495a3b5c517cd2bc0cb401ea0529f"><code>1d96c77</code></a> Add SSH user parameter (<a href="https://redirect.github.com/actions/checkout/issues/1685">dart-lang/args#1685</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/cd7d8d697e10461458bc61a30d094dc601a8b017"><code>cd7d8d6</code></a> Check git version before attempting to disable <code>sparse-checkout</code> (<a href="https://redirect.github.com/actions/checkout/issues/1656">dart-lang/args#1656</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/8410ad0602e1e429cee44a835ae9f77f654a6694"><code>8410ad0</code></a> Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> (<a href="https://redirect.github.com/actions/checkout/issues/1650">dart-lang/args#1650</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/9bb56186c3b09b4f86b1c65136769dd318469633...0ad4b8fadaa221de15dcec353f45205ec38ea70b">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
</details>
* blast_repo fixes (dart-lang/args#274)
dependabot
* bump lints dep and fix (dart-lang/args#275)
* Bump actions/checkout from 4.1.4 to 4.1.6 in the github-actions group (dart-lang/args#276)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).
Updates `actions/checkout` from 4.1.4 to 4.1.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.6</h2>
<h2>What's Changed</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkoutdart-lang/args#1732</a></li>
<li>Update for 4.1.6 release by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1733">actions/checkoutdart-lang/args#1733</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.5...v4.1.6">https://github.com/actions/checkout/compare/v4.1.5...v4.1.6</a></p>
<h2>v4.1.5</h2>
<h2>What's Changed</h2>
<ul>
<li>Update NPM dependencies by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkoutdart-lang/args#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkoutdart-lang/args#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkoutdart-lang/args#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkoutdart-lang/args#1695</a></li>
<li>README: Suggest <code>user.email</code> to be <code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkoutdart-lang/args#1707</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.4...v4.1.5">https://github.com/actions/checkout/compare/v4.1.4...v4.1.5</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkoutdart-lang/args#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkoutdart-lang/args#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkoutdart-lang/args#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkoutdart-lang/args#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkoutdart-lang/args#1695</a></li>
<li>README: Suggest <code>user.email</code> to be <code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkoutdart-lang/args#1707</a></li>
</ul>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkoutdart-lang/args#1692</a></li>
<li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkoutdart-lang/args#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkoutdart-lang/args#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkoutdart-lang/args#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<ul>
<li>Check git version before attempting to disable <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1656">actions/checkoutdart-lang/args#1656</a></li>
<li>Add SSH user parameter by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1685">actions/checkoutdart-lang/args#1685</a></li>
<li>Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1650">actions/checkoutdart-lang/args#1650</a></li>
</ul>
<h2>v4.1.2</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
</ul>
<h2>v4.1.1</h2>
<ul>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/a5ac7e51b41094c92402da3b24376905380afc29"><code>a5ac7e5</code></a> Update for 4.1.6 release (<a href="https://redirect.github.com/actions/checkout/issues/1733">dart-lang/args#1733</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/24ed1a352802348c9e4e8d13de9177fb95b537ba"><code>24ed1a3</code></a> Check platform for extension (<a href="https://redirect.github.com/actions/checkout/issues/1732">dart-lang/args#1732</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/44c2b7a8a4ea60a981eaca3cf939b5f4305c123b"><code>44c2b7a</code></a> README: Suggest <code>user.email</code> to be `41898282+github-actions[bot]<a href="https://github.com/users"><code>@âusers</code></a>.norepl...</li>
<li><a href="https://github.com/actions/checkout/commit/8459bc0c7e3759cdf591f513d9f141a95fef0a8f"><code>8459bc0</code></a> Bump actions/upload-artifact from 2 to 4 (<a href="https://redirect.github.com/actions/checkout/issues/1695">dart-lang/args#1695</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/3f603f6d5e9f40714f97b2f017aa0df2a443192a"><code>3f603f6</code></a> Bump actions/setup-node from 1 to 4 (<a href="https://redirect.github.com/actions/checkout/issues/1696">dart-lang/args#1696</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/fd084cde189b7b76ec305d52e27be545a0172823"><code>fd084cd</code></a> Bump github/codeql-action from 2 to 3 (<a href="https://redirect.github.com/actions/checkout/issues/1694">dart-lang/args#1694</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/9c1e94e0ad997d618b6113a2171b055037589028"><code>9c1e94e</code></a> Update NPM dependencies (<a href="https://redirect.github.com/actions/checkout/issues/1703">dart-lang/args#1703</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/0ad4b8fadaa221de15dcec353f45205ec38ea70b...a5ac7e51b41094c92402da3b24376905380afc29">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions
</details>
* Bump the github-actions group with 2 updates (dart-lang/args#278)
Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart).
Updates `actions/checkout` from 4.1.6 to 4.1.7
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.1.7</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkoutdart-lang/args#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkoutdart-lang/args#1697</a></li>
<li>Check out other refs/* by commit by <a href="https://github.com/orhantoy"><code>@âorhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkoutdart-lang/args#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable version. by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkoutdart-lang/args#1776</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/orhantoy"><code>@âorhantoy</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkoutdart-lang/args#1774</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.6...v4.1.7">https://github.com/actions/checkout/compare/v4.1.6...v4.1.7</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkoutdart-lang/args#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkoutdart-lang/args#1697</a></li>
<li>Check out other refs/* by commit by <a href="https://github.com/orhantoy"><code>@âorhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkoutdart-lang/args#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable version. by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkoutdart-lang/args#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkoutdart-lang/args#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkoutdart-lang/args#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkoutdart-lang/args#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkoutdart-lang/args#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkoutdart-lang/args#1695</a></li>
<li>README: Suggest <code>user.email</code> to be <code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkoutdart-lang/args#1707</a></li>
</ul>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkoutdart-lang/args#1692</a></li>
<li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkoutdart-lang/args#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkoutdart-lang/args#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkoutdart-lang/args#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<ul>
<li>Check git version before attempting to disable <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1656">actions/checkoutdart-lang/args#1656</a></li>
<li>Add SSH user parameter by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1685">actions/checkoutdart-lang/args#1685</a></li>
<li>Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1650">actions/checkoutdart-lang/args#1650</a></li>
</ul>
<h2>v4.1.2</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
</ul>
<h2>v4.1.1</h2>
<ul>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth > 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/692973e3d937129bcbf40652eb9f2f61becf3332"><code>692973e</code></a> Prepare 4.1.7 release (<a href="https://redirect.github.com/actions/checkout/issues/1775">dart-lang/args#1775</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/6ccd57f4c5d15bdc2fef309bd9fb6cc9db2ef1c6"><code>6ccd57f</code></a> Pin actions/checkout's own workflows to a known, good, stable version. (<a href="https://redirect.github.com/actions/checkout/issues/1776">dart-lang/args#1776</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/b17fe1e4d59a9d1d95a7aead5e6fcd13e50939a5"><code>b17fe1e</code></a> Handle hidden refs (<a href="https://redirect.github.com/actions/checkout/issues/1774">dart-lang/args#1774</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/b80ff79f1755d06ba70441c368a6fe801f5f3a62"><code>b80ff79</code></a> Bump actions/checkout from 3 to 4 (<a href="https://redirect.github.com/actions/checkout/issues/1697">dart-lang/args#1697</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/b1ec3021b8fa02164da82ca1557d017d83b0e179"><code>b1ec302</code></a> Bump the minor-npm-dependencies group across 1 directory with 4 updates (<a href="https://redirect.github.com/actions/checkout/issues/1739">dart-lang/args#1739</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/a5ac7e51b41094c92402da3b24376905380afc29...692973e3d937129bcbf40652eb9f2f61becf3332">compare view</a></li>
</ul>
</details>
<br />
Updates `dart-lang/setup-dart` from 1.6.4 to 1.6.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/releases">dart-lang/setup-dart's releases</a>.</em></p>
<blockquote>
<h2>v1.6.5</h2>
<ul>
<li>Fix zip path handling on Windows 11 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/args#118</a>[])</li>
</ul>
<p><a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/args#118</a>: <a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/setup-dartdart-lang/args#118</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md">dart-lang/setup-dart's changelog</a>.</em></p>
<blockquote>
<h2>v1.6.5</h2>
<ul>
<li>Fix zip path handling on Windows 11 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/args#118</a>[])</li>
</ul>
<p><a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/args#118</a>: <a href="https://redirect.github.com/dart-lang/setup-dart/issues/118">dart-lang/setup-dartdart-lang/args#118</a></p>
<h2>v1.6.4</h2>
<ul>
<li>Rebuild JS code.</li>
</ul>
<h2>v1.6.3</h2>
<ul>
<li>Roll <code>undici</code> dependency to address <a href="https://github.com/nodejs/undici/security/advisories/GHSA-m4v8-wqvr-p9f7">CVE-2024-30260</a> and <a href="https://github.com/nodejs/undici/security/advisories/GHSA-9qxr-qj54-h672">CVE-2024-30261</a>.</li>
</ul>
<h2>v1.6.2</h2>
<ul>
<li>Switch to running the workflow on <code>node20`` from </code>node16`. See also
<a href="https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/">Transitioning from Node 16 to Node 20</a>.</li>
</ul>
<h2>v1.6.1</h2>
<ul>
<li>Updated the google storage url for <code>main</code> channel releases.</li>
</ul>
<h2>v1.6.0</h2>
<ul>
<li>Enable provisioning of the latest Dart SDK patch release by specifying just
the major and minor version (e.g. <code>3.2</code>).</li>
</ul>
<h2>v1.5.1</h2>
<ul>
<li>No longer test the <code>setup-dart</code> action on pre-2.12 SDKs.</li>
<li>Upgrade JS interop code to use extension types
(the new name for inline classes).</li>
<li>The upcoming rename of the <code>be</code> channel to <code>main</code> is now supported with
forward compatibility that switches when the rename happens.</li>
</ul>
<h2>v1.5.0</h2>
<ul>
<li>Re-wrote the implementation of the action into Dart.</li>
<li>Auto-detect the platform architecture (<code>x64</code>, <code>ia32</code>, <code>arm</code>, <code>arm64</code>).</li>
<li>Improved the caching and download resilience of the sdk.</li>
<li>Added a new action output: <code>dart-version</code> - the installed version of the sdk.</li>
</ul>
<h2>v1.4.0</h2>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/dart-lang/setup-dart/commit/0a8a0fc875eb934c15d08629302413c671d3f672"><code>0a8a0fc</code></a> rev to v1.6.5 in prep for publishing (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/135">dart-lang/args#135</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/4e61f04f255b227a0511bf9ef0f5ddc7c8542531"><code>4e61f04</code></a> fix: Use <code>.zip</code> in archive path (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/119">dart-lang/args#119</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/2986c8e337a31eb7b455ce93dc984e9bf5797756"><code>2986c8e</code></a> blast_repo fixes (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/132">dart-lang/args#132</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/40a7d12289757ab3aaa39d47d6f99a9cb1dd701e"><code>40a7d12</code></a> Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/131">dart-lang/args#131</a>)</li>
<li><a href="https://github.com/dart-lang/setup-dart/commit/40f1ce0b1c8f24a4e9cb7eb026e4dbbdb431eb76"><code>40f1ce0</code></a> Update dart.yml (<a href="https://redirect.github.com/dart-lang/setup-dart/issues/130">dart-lang/args#130</a>)</li>
<li>See full diff in <a href="https://github.com/dart-lang/setup-dart/compare/f0ead981b4d9a35b37f30d36160575d60931ec30...0a8a0fc875eb934c15d08629302413c671d3f672">compare view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions
</details>
* Fix angle brackets in doc comments (dart-lang/args#281)
* Add argument name when throwing a `ArgParserException`. (dart-lang/args#283)
Add an `argumentName` field which tracks the argument that was being
parse when the exception is thrown.
* Bump actions/checkout from 4.1.7 to 4.2.0 in the github-actions group (dart-lang/args#286)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).
Updates `actions/checkout` from 4.1.7 to 4.2.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.2.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Add Ref and Commit outputs by <a href="https://github.com/lucacome"><code>@âlucacome</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkoutdart-lang/args#1180</a></li>
<li>Dependabot updates in <a href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkoutdart-lang/args#1777</a> & <a href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkoutdart-lang/args#1872</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/yasonk"><code>@âyasonk</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1869">actions/checkoutdart-lang/args#1869</a></li>
<li><a href="https://github.com/lucacome"><code>@âlucacome</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkoutdart-lang/args#1180</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.1.7...v4.2.0">https://github.com/actions/checkout/compare/v4.1.7...v4.2.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a href="https://github.com/lucacome"><code>@âlucacome</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkoutdart-lang/args#1180</a></li>
<li>Dependency updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a>- <a href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkoutdart-lang/args#1777</a>, <a href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkoutdart-lang/args#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkoutdart-lang/args#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkoutdart-lang/args#1697</a></li>
<li>Check out other refs/* by commit by <a href="https://github.com/orhantoy"><code>@âorhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkoutdart-lang/args#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable version. by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkoutdart-lang/args#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkoutdart-lang/args#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkoutdart-lang/args#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkoutdart-lang/args#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkoutdart-lang/args#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkoutdart-lang/args#1695</a></li>
<li>README: Suggest <code>user.email</code> to be <code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkoutdart-lang/args#1707</a></li>
</ul>
<h2>v4.1.4</h2>
<ul>
<li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkoutdart-lang/args#1692</a></li>
<li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkoutdart-lang/args#1688</a></li>
<li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkoutdart-lang/args#1693</a></li>
<li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@âdependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkoutdart-lang/args#1643</a></li>
</ul>
<h2>v4.1.3</h2>
<ul>
<li>Check git version before attempting to disable <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1656">actions/checkoutdart-lang/args#1656</a></li>
<li>Add SSH user parameter by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1685">actions/checkoutdart-lang/args#1685</a></li>
<li>Update <code>actions/checkout</code> version in <code>update-main-version.yml</code> by <a href="https://github.com/jww3"><code>@âjww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1650">actions/checkoutdart-lang/args#1650</a></li>
</ul>
<h2>v4.1.2</h2>
<ul>
<li>Fix: Disable sparse checkout whenever <code>sparse-checkout</code> option is not present <a href="https://github.com/dscho"><code>@âdscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1598">actions/checkoutdart-lang/args#1598</a></li>
</ul>
<h2>v4.1.1</h2>
<ul>
<li>Correct link to GitHub Docs by <a href="https://github.com/peterbe"><code>@âpeterbe</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1511">actions/checkoutdart-lang/args#1511</a></li>
<li>Link to release page from what's new section by <a href="https://github.com/cory-miller"><code>@âcory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1514">actions/checkoutdart-lang/args#1514</a></li>
</ul>
<h2>v4.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1396">Add support for partial checkout filters</a></li>
</ul>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
</ul>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/d632683dd7b4114ad314bca15554477dd762a938"><code>d632683</code></a> Prepare 4.2.0 release (<a href="https://redirect.github.com/actions/checkout/issues/1878">dart-lang/args#1878</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/6d193bf28034eafb982f37bd894289fe649468fc"><code>6d193bf</code></a> Bump braces from 3.0.2 to 3.0.3 (<a href="https://redirect.github.com/actions/checkout/issues/1777">dart-lang/args#1777</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/db0cee9a514becbbd4a101a5fbbbf47865ee316c"><code>db0cee9</code></a> Bump the minor-npm-dependencies group across 1 directory with 4 updates (<a href="https://redirect.github.com/actions/checkout/issues/1872">dart-lang/args#1872</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/b6849436894e144dbce29d7d7fda2ae3bf9d8365"><code>b684943</code></a> Add Ref and Commit outputs (<a href="https://redirect.github.com/actions/checkout/issues/1180">dart-lang/args#1180</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/2d7d9f7ff5b310f983d059b68785b3c74d8b8edd"><code>2d7d9f7</code></a> Provide explanation for where user email came from (<a href="https://redirect.github.com/actions/checkout/issues/1869">dart-lang/args#1869</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/9a9194f87191a7e9055e3e9b95b8cfb13023bb08"><code>9a9194f</code></a> Bump docker/build-push-action from 5.3.0 to 6.5.0 (<a href="https://redirect.github.com/actions/checkout/issues/1832">dart-lang/args#1832</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/dd960bd3c3f080561a1810e32349ac211ecec7d4"><code>dd960bd</code></a> Bump docker/login-action in the minor-actions-dependencies group (<a href="https://redirect.github.com/actions/checkout/issues/1831">dart-lang/args#1831</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938">compare view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions
</details>
* Fix workflows
* Fix workflows again
* Rev pubspec version
* Fix tags regex
* Add readme
* Add label workflow
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: rnystrom@google.com <rnystrom@google.com>
Co-authored-by: amouravski@google.com <amouravski@google.com>
Co-authored-by: dgrove@google.com <dgrove@google.com>
Co-authored-by: floitsch@google.com <floitsch@google.com>
Co-authored-by: sigmund@google.com <sigmund@google.com>
Co-authored-by: lrn@google.com <lrn@google.com>
Co-authored-by: kevmoo@j832.com <kevmoo@j832.com>
Co-authored-by: ajohnsen@google.com <ajohnsen@google.com>
Co-authored-by: sethladd@google.com <sethladd@google.com>
Co-authored-by: antonm@google.com <antonm@google.com>
Co-authored-by: kathyw@google.com <kathyw@google.com>
Co-authored-by: nweiz@google.com <nweiz@google.com>
Co-authored-by: whesse@google.com <whesse@google.com>
Co-authored-by: jmesserly@google.com <jmesserly@google.com>
Co-authored-by: kevmoo@google.com <kevmoo@google.com>
Co-authored-by: srawlins@google.com <srawlins@google.com>
Co-authored-by: Kevin Moore <github@j832.com>
Co-authored-by: Jacob Richman <jacobr@google.com>
Co-authored-by: Seth Ladd <sethladd@gmail.com>
Co-authored-by: Oliver <olli.sand@googlemail.com>
Co-authored-by: Kenneth Endfinger <kaendfinger@gmail.com>
Co-authored-by: Natalie Weizenbaum <nex342@gmail.com>
Co-authored-by: Kasper Peulen <kasperpeulen@gmail.com>
Co-authored-by: José Luis Cruz <joseluis@users.noreply.github.com>
Co-authored-by: ArgentiApparatus <t6iz8t48@gmail.com>
Co-authored-by: Kevin Moore <kevmoo@users.noreply.github.com>
Co-authored-by: Leaf Petersen <leafpetersen@users.noreply.github.com>
Co-authored-by: BC Ko <bcko@users.noreply.github.com>
Co-authored-by: Bob Nystrom <robert@stuffwithstuff.com>
Co-authored-by: Sam Rawlins <sam.rawlins@gmail.com>
Co-authored-by: Patrice Chalin <chalin@users.noreply.github.com>
Co-authored-by: Greg Spencer <gspencer@google.com>
Co-authored-by: Todd Volkert <tvolkert@users.noreply.github.com>
Co-authored-by: Phil Quitslund <pq@users.noreply.github.com>
Co-authored-by: Nate Bosch <nbosch1@gmail.com>
Co-authored-by: David Morgan <davidmorgan@google.com>
Co-authored-by: Devon Carew <devoncarew@google.com>
Co-authored-by: Jacob MacDonald <jakemac@google.com>
Co-authored-by: Brett Sutton <bsutton@noojee.com.au>
Co-authored-by: Nate Bosch <nbosch@google.com>
Co-authored-by: Alexander Thomas <athom@google.com>
Co-authored-by: JM REMEUR <jm@remeur.fr>
Co-authored-by: Franklin Yow <58489007+franklinyow@users.noreply.github.com>
Co-authored-by: Pierre-Louis <6655696+guidezpl@users.noreply.github.com>
Co-authored-by: Konstantin Scheglov <scheglov@google.com>
Co-authored-by: TheHackerCoding <29006605+TheHackerCoding@users.noreply.github.com>
Co-authored-by: Kato Shinya <kato.shinya.dev@gmail.com>
Co-authored-by: Liam Appelbe <liama@google.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sigurd Meldgaard <sigurdm@google.com>
Co-authored-by: Naoto Kaneko <naoty.k@gmail.com>
Co-authored-by: Albert Kaiser <ak@kaiserv.de>
Co-authored-by: ふぁ <34892635+fa0311@users.noreply.github.com>
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
new file mode 100644
index 0000000..cde02ad
--- /dev/null
+++ b/.github/dependabot.yaml
@@ -0,0 +1,15 @@
+# Dependabot configuration file.
+# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates
+version: 2
+
+updates:
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: monthly
+ labels:
+ - autosubmit
+ groups:
+ github-actions:
+ patterns:
+ - "*"
diff --git a/.github/labeler.yaml b/.github/labeler.yaml
new file mode 100644
index 0000000..074dd1f
--- /dev/null
+++ b/.github/labeler.yaml
@@ -0,0 +1,5 @@
+# Configuration for .github/workflows/pull_request_label.yml.
+
+"package-args":
+ - changed-files:
+ - any-glob-to-any-file: 'pkgs/args/**'
diff --git a/.github/workflows/args.yaml b/.github/workflows/args.yaml
new file mode 100644
index 0000000..dc75893
--- /dev/null
+++ b/.github/workflows/args.yaml
@@ -0,0 +1,72 @@
+name: Dart CI
+
+on:
+ # Run CI on pushes to the main branch, and on PRs against main.
+ push:
+ branches: [ main ]
+ paths:
+ - '.github/workflows/args.yaml'
+ - 'pkgs/args/**'
+ pull_request:
+ branches: [ main ]
+ paths:
+ - '.github/workflows/args.yaml'
+ - 'pkgs/args/**'
+ schedule:
+ - cron: "0 0 * * 0"
+env:
+ PUB_ENVIRONMENT: bot.github
+
+defaults:
+ run:
+ working-directory: pkgs/args/
+
+jobs:
+ # Check code formatting and static analysis on a single OS (linux)
+ # against Dart dev.
+ analyze:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ sdk: [dev]
+ steps:
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
+ - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
+ with:
+ sdk: ${{ matrix.sdk }}
+ - id: install
+ name: Install dependencies
+ run: dart pub get
+ - name: Check formatting
+ if: always() && steps.install.outcome == 'success'
+ run: dart format --output=none --set-exit-if-changed .
+ - name: Analyze code
+ run: dart analyze --fatal-infos
+ if: always() && steps.install.outcome == 'success'
+
+ # Run tests on a matrix consisting of three dimensions:
+ # 1. OS: ubuntu-latest, (macos-latest, windows-latest)
+ # 2. release channel: dev, (stable)
+ test:
+ needs: analyze
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest]
+ sdk: ['3.3', dev]
+ steps:
+ - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938
+ - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
+ with:
+ sdk: ${{ matrix.sdk }}
+ - id: install
+ name: Install dependencies
+ run: dart pub get
+ - name: Run VM tests
+ run: dart test --platform vm
+ if: always() && steps.install.outcome == 'success'
+ - name: Run Chrome tests
+ run: dart test --platform chrome
+ if: always() && steps.install.outcome == 'success'
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
new file mode 100644
index 0000000..205a02d
--- /dev/null
+++ b/.github/workflows/publish.yaml
@@ -0,0 +1,14 @@
+# A CI configuration to auto-publish pub packages.
+
+name: Publish
+
+on:
+ pull_request:
+ branches: [ main ]
+ push:
+ tags: [ '[A-z]+-v[0-9]+.[0-9]+.[0-9]+' ]
+
+jobs:
+ publish:
+ if: ${{ github.repository_owner == 'dart-lang' }}
+ uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main
diff --git a/.github/workflows/pull_request_label.yaml b/.github/workflows/pull_request_label.yaml
new file mode 100644
index 0000000..54e3df5
--- /dev/null
+++ b/.github/workflows/pull_request_label.yaml
@@ -0,0 +1,22 @@
+# This workflow applies labels to pull requests based on the paths that are
+# modified in the pull request.
+#
+# Edit `.github/labeler.yml` to configure labels. For more information, see
+# https://github.com/actions/labeler.
+
+name: Pull Request Labeler
+permissions: read-all
+
+on:
+ pull_request_target
+
+jobs:
+ label:
+ permissions:
+ pull-requests: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ sync-labels: true
diff --git a/README.md b/README.md
index 2fbc603..7b81d0b 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@
| Package | Description | Version |
|---|---|---|
+| [args](pkgs/args/) | Library for defining parsers for parsing raw command-line arguments into a set
+ of options and values. | [](https://pub.dev/packages/args) |
## Publishing automation
@@ -15,4 +17,4 @@
https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.
For additional information about contributing, see our
-[contributing](CONTRIBUTING.md) page.
\ No newline at end of file
+[contributing](CONTRIBUTING.md) page.
diff --git a/pkgs/args/.gitignore b/pkgs/args/.gitignore
new file mode 100644
index 0000000..813a31e
--- /dev/null
+++ b/pkgs/args/.gitignore
@@ -0,0 +1,16 @@
+# Don’t commit the following directories created by pub.
+.buildlog
+.pub/
+.dart_tool/
+build/
+packages
+.packages
+
+# Or the files created by dart2js.
+*.dart.js
+*.js_
+*.js.deps
+*.js.map
+
+# Include when developing application packages.
+pubspec.lock
diff --git a/pkgs/args/.test_config b/pkgs/args/.test_config
new file mode 100644
index 0000000..412fc5c
--- /dev/null
+++ b/pkgs/args/.test_config
@@ -0,0 +1,3 @@
+{
+ "test_package": true
+}
\ No newline at end of file
diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md
new file mode 100644
index 0000000..15b392b
--- /dev/null
+++ b/pkgs/args/CHANGELOG.md
@@ -0,0 +1,346 @@
+## 2.6.0
+
+* Added source argument when throwing a `ArgParserException`.
+* Fix inconsistent `FormatException` messages
+* Require Dart 3.3
+* Move to `dart-lang/core` monorepo.
+
+## 2.5.0
+
+* Introduce new typed `ArgResults` `flag(String)`, `option(String)`, and
+ `multiOption(String)` methods.
+* Require Dart 3.0.
+
+## 2.4.2
+
+* Change the validation of `mandatory` options; they now perform validation when
+ the value is retrieved (from the `ArgResults` object), instead of when the
+ args are parsed.
+* Require Dart 2.19.
+
+## 2.4.1
+
+* Add a `CONTRIBUTING.md` file; move the publishing automation docs from the
+ readme into the contributing doc.
+* Added package topics to the pubspec file.
+
+## 2.4.0
+
+* Command suggestions will now also suggest based on aliases of a command.
+* Introduce getter `Command.suggestionAliases` for names that cannot be used as
+ aliases, but will trigger suggestions.
+
+## 2.3.2
+
+* Require Dart 2.18
+
+## 2.3.1
+
+* Switch to using package:lints.
+* Address an issue with the readme API documentation (#211).
+* Populate the pubspec `repository` field.
+
+## 2.3.0
+
+* Add the ability to group commands by category in usage text.
+
+## 2.2.0
+
+* Suggest similar commands if an unknown command is encountered, when using the
+ `CommandRunner`.
+ * The max edit distance for suggestions defaults to 2, but can be configured
+ using the `suggestionDistanceLimit` parameter on the constructor. You can
+ set it to `0` to disable the feature.
+
+## 2.1.1
+
+* Fix a bug with `mandatory` options which caused a null assertion failure when
+ used within a command.
+
+## 2.1.0
+
+* Add a `mandatory` argument to require the presence of an option.
+* Add `aliases` named argument to `addFlag`, `addOption`, and `addMultiOption`,
+ as well as a public `findByNameOrAlias` method on `ArgParser`. This allows
+ you to provide aliases for an argument name, which eases the transition from
+ one argument name to another.
+
+## 2.0.0
+
+* Stable null safety release.
+
+## 2.0.0-nullsafety.0
+
+* Migrate to null safety.
+* **BREAKING** Remove APIs that had been marked as deprecated:
+
+ * Instead of the `allowMulti` and `splitCommas` arguments to
+ `ArgParser.addOption()`, use `ArgParser.addMultiOption()`.
+ * Instead of `ArgParser.getUsage()`, use `ArgParser.usage`.
+ * Instead of `Option.abbreviation`, use `Option.abbr`.
+ * Instead of `Option.defaultValue`, use `Option.defaultsTo`.
+ * Instead of `OptionType.FLAG/SINGLE/MULTIPLE`, use
+ `OptionType.flag/single/multiple`.
+* Add a more specific function type to the `callback` argument of `addOption`.
+
+## 1.6.0
+
+* Remove `help` from the list of commands in usage.
+* Remove the blank lines in usage which separated the help for options that
+ happened to span multiple lines.
+
+## 1.5.4
+
+* Fix a bug with option names containing underscores.
+* Point towards `CommandRunner` in the docs for `ArgParser.addCommand` since it
+ is what most authors will want to use instead.
+
+## 1.5.3
+
+* Improve arg parsing performance: use queues instead of lists internally to
+ get linear instead of quadratic performance, which is important for large
+ numbers of args (>1000). And, use simple string manipulation instead of
+ regular expressions for a 1.5x improvement everywhere.
+* No longer automatically add a 'help' option to commands that don't validate
+ their arguments (fix #123).
+
+## 1.5.2
+
+* Added support for `usageLineLength` in `CommandRunner`
+
+## 1.5.1
+
+* Added more comprehensive word wrapping when `usageLineLength` is set.
+
+## 1.5.0
+
+* Add `usageLineLength` to control word wrapping usage text.
+
+## 1.4.4
+
+* Set max SDK version to `<3.0.0`, and adjust other dependencies.
+
+## 1.4.3
+
+* Display the default values for options with `allowedHelp` specified.
+
+## 1.4.2
+
+* Narrow the SDK constraint to only allow SDK versions that support `FutureOr`.
+
+## 1.4.1
+
+* Fix the way default values for multi-valued options are printed in argument
+ usage.
+
+## 1.4.0
+
+* Deprecated `OptionType.FLAG`, `OptionType.SINGLE`, and `OptionType.MULTIPLE`
+ in favor of `OptionType.flag`, `OptionType.single`, and `OptionType.multiple`
+ which follow the style guide.
+
+* Deprecated `Option.abbreviation` and `Option.defaultValue` in favor of
+ `Option.abbr` and `Option.defaultsTo`. This makes all of `Option`'s fields
+ match the corresponding parameters to `ArgParser.addOption()`.
+
+* Deprecated the `allowMultiple` and `splitCommas` arguments to
+ `ArgParser.addOption()` in favor of a separate `ArgParser.addMultiOption()`
+ method. This allows us to provide more accurate type information, and to avoid
+ adding flags that only make sense for multi-options in places where they might
+ be usable for single-value options.
+
+## 1.3.0
+
+* Type `Command.run()`'s return value as `FutureOr<T>`.
+
+## 1.2.0
+
+* Type the `callback` parameter to `ArgParser.addOption()` as `Function` rather
+ than `void Function(value)`. This allows strong-mode users to write `callback:
+ (String value) { ... }` rather than having to manually cast `value` to a
+ `String` (or a `List<String>` with `allowMultiple: true`).
+
+## 1.1.0
+
+* `ArgParser.parse()` now takes an `Iterable<String>` rather than a
+ `List<String>`.
+
+* `ArgParser.addOption()`'s `allowed` option now takes an `Iterable<String>`
+ rather than a `List<String>`.
+
+## 1.0.2
+
+* Fix analyzer warning
+
+## 1.0.1
+
+* Fix a fuzzy arrow type warning.
+
+## 1.0.0
+
+* **Breaking change**: The `allowTrailingOptions` argument to `new
+ ArgumentParser()` defaults to `true` instead of `false`.
+
+* Add `new ArgParser.allowAnything()`. This allows any input, without parsing
+ any options.
+
+## 0.13.7
+
+* Add explicit support for forwarding the value returned by `Command.run()` to
+ `CommandRunner.run()`. This worked unintentionally prior to 0.13.6+1.
+
+* Add type arguments to `CommandRunner` and `Command` to indicate the return
+ values of the `run()` functions.
+
+## 0.13.6+1
+
+* When a `CommandRunner` is passed `--help` before any commands, it now prints
+ the usage of the chosen command.
+
+## 0.13.6
+
+* `ArgParser.parse()` now throws an `ArgParserException`, which implements
+ `FormatException` and has a field that lists the commands that were parsed.
+
+* If `CommandRunner.run()` encounters a parse error for a subcommand, it now
+ prints the subcommand's usage rather than the global usage.
+
+## 0.13.5
+
+* Allow `CommandRunner.argParser` and `Command.argParser` to be overridden in
+ strong mode.
+
+## 0.13.4+2
+
+* Fix a minor documentation error.
+
+## 0.13.4+1
+
+* Ensure that multiple-value arguments produce reified `List<String>`s.
+
+## 0.13.4
+
+* By default, only the first line of a command's description is included in its
+ parent runner's usage string. This returns to the default behavior from
+ before 0.13.3+1.
+
+* A `Command.summary` getter has been added to explicitly control the summary
+ that appears in the parent runner's usage string. This getter defaults to the
+ first line of the description, but can be overridden if the user wants a
+ multi-line summary.
+
+## 0.13.3+6
+
+* README fixes.
+
+## 0.13.3+5
+
+* Make strong mode clean.
+
+## 0.13.3+4
+
+* Use the proper `usage` getter in the README.
+
+## 0.13.3+3
+
+* Add an explicit default value for the `allowTrailingOptions` parameter to `new
+ ArgParser()`. This doesn't change the behavior at all; the option already
+ defaulted to `false`, and passing in `null` still works.
+
+## 0.13.3+2
+
+* Documentation fixes.
+
+## 0.13.3+1
+
+* Print all lines of multi-line command descriptions.
+
+## 0.13.2
+
+* Allow option values that look like options. This more closely matches the
+ behavior of [`getopt`][getopt], the *de facto* standard for option parsing.
+
+[getopt]: https://man7.org/linux/man-pages/man3/getopt.3.html
+
+## 0.13.1
+
+* Add `ArgParser.addSeparator()`. Separators allow users to group their options
+ in the usage text.
+
+## 0.13.0
+
+* **Breaking change**: An option that allows multiple values will now
+ automatically split apart comma-separated values. This can be controlled with
+ the `splitCommas` option.
+
+## 0.12.2+6
+
+* Remove the dependency on the `collection` package.
+
+## 0.12.2+5
+
+* Add syntax highlighting to the README.
+
+## 0.12.2+4
+
+* Add an example of using command-line arguments to the README.
+
+## 0.12.2+3
+
+* Fixed implementation of ArgResults.options to really use Iterable<String>
+ instead of Iterable<dynamic> cast to Iterable<String>.
+
+## 0.12.2+2
+
+* Updated dependency constraint on `unittest`.
+
+* Formatted source code.
+
+* Fixed use of deprecated API in example.
+
+## 0.12.2+1
+
+* Fix the built-in `help` command for `CommandRunner`.
+
+## 0.12.2
+
+* Add `CommandRunner` and `Command` classes which make it easy to build a
+ command-based command-line application.
+
+* Add an `ArgResults.arguments` field, which contains the original argument list.
+
+## 0.12.1
+
+* Replace `ArgParser.getUsage()` with `ArgParser.usage`, a getter.
+ `ArgParser.getUsage()` is now deprecated, to be removed in args version 1.0.0.
+
+## 0.12.0+2
+
+* Widen the version constraint on the `collection` package.
+
+## 0.12.0+1
+
+* Remove the documentation link from the pubspec so this is linked to
+ pub.dev by default.
+
+## 0.12.0
+
+* Removed public constructors for `ArgResults` and `Option`.
+
+* `ArgResults.wasParsed()` can be used to determine if an option was actually
+ parsed or the default value is being returned.
+
+* Replaced `isFlag` and `allowMultiple` fields in the `Option` class with a
+ three-value `OptionType` enum.
+
+* Options may define `valueHelp` which will then be shown in the usage.
+
+## 0.11.0
+
+* Move handling trailing options from `ArgParser.parse()` into `ArgParser`
+ itself. This lets subcommands have different behavior for how they handle
+ trailing options.
+
+## 0.10.0+2
+
+* Usage ignores hidden options when determining column widths.
diff --git a/pkgs/args/CONTRIBUTING.md b/pkgs/args/CONTRIBUTING.md
new file mode 100644
index 0000000..d8db4ac
--- /dev/null
+++ b/pkgs/args/CONTRIBUTING.md
@@ -0,0 +1,56 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement (CLA). You (or your employer) retain the copyright to your
+contribution; this simply gives us permission to use and redistribute your
+contributions as part of the project. Head over to
+<https://cla.developers.google.com/> to see your current agreements on file or
+to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code Reviews
+
+All submissions, including submissions by project members, require review. We
+use GitHub pull requests for this purpose. Consult
+[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
+information on using pull requests.
+
+## Coding style
+
+The Dart source code in this repo follows the:
+
+ * [Dart style guide](https://dart.dev/guides/language/effective-dart/style)
+
+You should familiarize yourself with those guidelines.
+
+## File headers
+
+All files in the Dart project must start with the following header; if you add a
+new file please also add this. The year should be a single number stating the
+year the file was created (don't use a range like "2011-2012"). Additionally, if
+you edit an existing file, you shouldn't update the year.
+
+ // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+ // for details. All rights reserved. Use of this source code is governed by a
+ // BSD-style license that can be found in the LICENSE file.
+
+## Publishing automation
+
+For information about our publishing automation and release process, see
+https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
+
+We pledge to maintain an open and welcoming environment. For details, see our
+[code of conduct](https://dart.dev/code-of-conduct).
diff --git a/pkgs/args/LICENSE b/pkgs/args/LICENSE
new file mode 100644
index 0000000..ab3bfa0
--- /dev/null
+++ b/pkgs/args/LICENSE
@@ -0,0 +1,27 @@
+Copyright 2013, the Dart project authors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google LLC nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pkgs/args/README.md b/pkgs/args/README.md
new file mode 100644
index 0000000..05f36eb
--- /dev/null
+++ b/pkgs/args/README.md
@@ -0,0 +1,459 @@
+[](https://github.com/dart-lang/core/actions/workflows/args.yaml)
+[](https://pub.dev/packages/args)
+[](https://pub.dev/packages/args/publisher)
+
+Parses raw command-line arguments into a set of options and values.
+
+This library supports [GNU][] and [POSIX][] style options, and it works in both
+server-side and client-side apps.
+
+## Defining options
+
+First create an [ArgParser][]:
+
+ var parser = ArgParser();
+
+Then define a set of options on that parser using [addOption()][addOption] and
+[addFlag()][addFlag]. Here's the minimal way to create an option named "name":
+
+ parser.addOption('name');
+
+When an option can only be set or unset (as opposed to taking a string value),
+use a flag:
+
+```dart
+parser.addFlag('name');
+```
+
+Flag options, by default, accept a 'no-' prefix to negate the option. You can
+disable the 'no-' prefix using the `negatable` parameter:
+
+```dart
+parser.addFlag('name', negatable: false);
+```
+
+*Note:* From here on out, "option" refers to both regular options and flags. In
+cases where the distinction matters, we'll use "non-flag option."
+
+Options can have an optional single-character abbreviation, specified with the
+`abbr` parameter:
+
+```dart
+parser.addOption('mode', abbr: 'm');
+parser.addFlag('verbose', abbr: 'v');
+```
+
+Options can also have a default value, specified with the `defaultsTo`
+parameter. The default value is used when arguments don't specify the option.
+
+```dart
+parser.addOption('mode', defaultsTo: 'debug');
+parser.addFlag('verbose', defaultsTo: false);
+```
+
+The default value for non-flag options can be any string. For flags, it must
+be a `bool`.
+
+To validate a non-flag option, you can use the `allowed` parameter to provide an
+allowed set of values. When you do, the parser throws an
+[`ArgParserException`][ArgParserException] if the value for an option is not in
+the allowed set. Here's an example of specifying allowed values:
+
+```dart
+parser.addOption('mode', allowed: ['debug', 'release']);
+```
+
+You can use the `callback` parameter to associate a function with an option.
+Later, when parsing occurs, the callback function is invoked with the value of
+the option:
+
+```dart
+parser.addOption('mode', callback: (mode) => print('Got mode $mode'));
+parser.addFlag('verbose', callback: (verbose) {
+ if (verbose) print('Verbose');
+});
+```
+
+The callbacks for all options are called whenever a set of arguments is parsed.
+If an option isn't provided in the args, its callback is passed the default
+value, or `null` if no default value is set.
+
+If an option is `mandatory` but not provided, the results object throws an
+[`ArgumentError`][ArgumentError] on retrieval.
+
+```dart
+parser.addOption('mode', mandatory: true);
+```
+
+## Parsing arguments
+
+Once you have an [ArgParser][] set up with some options and flags, you use it by
+calling [ArgParser.parse()][parse] with a set of arguments:
+
+```dart
+var results = parser.parse(['some', 'command', 'line', 'args']);
+```
+
+These arguments usually come from the arguments to `main()`. For example:
+
+ main(List<String> args) {
+ // ...
+ var results = parser.parse(args);
+ }
+
+However, you can pass in any list of strings. The `parse()` method returns an
+instance of [ArgResults][], a map-like object that contains the values of the
+parsed options.
+
+```dart
+var parser = ArgParser();
+parser.addOption('mode');
+parser.addFlag('verbose', defaultsTo: true);
+var results = parser.parse(['--mode', 'debug', 'something', 'else']);
+
+print(results.option('mode')); // debug
+print(results.flag('verbose')); // true
+```
+
+By default, the `parse()` method allows additional flags and options to be
+passed after positional parameters unless `--` is used to indicate that all
+further parameters will be positional. The positional arguments go into
+[ArgResults.rest][rest].
+
+```dart
+print(results.rest); // ['something', 'else']
+```
+
+To stop parsing options as soon as a positional argument is found,
+`allowTrailingOptions: false` when creating the [ArgParser][].
+
+## Specifying options
+
+To actually pass in options and flags on the command line, use GNU or POSIX
+style. Consider this option:
+
+```dart
+parser.addOption('name', abbr: 'n');
+```
+
+You can specify its value on the command line using any of the following:
+
+```
+--name=somevalue
+--name somevalue
+-nsomevalue
+-n somevalue
+```
+
+Consider this flag:
+
+```dart
+parser.addFlag('name', abbr: 'n');
+```
+
+You can set it to true using one of the following:
+
+```
+--name
+-n
+```
+
+You can set it to false using the following:
+
+```
+--no-name
+```
+
+Multiple flag abbreviations can be collapsed into a single argument. Say you
+define these flags:
+
+```dart
+parser
+ ..addFlag('verbose', abbr: 'v')
+ ..addFlag('french', abbr: 'f')
+ ..addFlag('iambic-pentameter', abbr: 'i');
+```
+
+You can set all three flags at once:
+
+```
+-vfi
+```
+
+By default, an option has only a single value, with later option values
+overriding earlier ones; for example:
+
+```dart
+var parser = ArgParser();
+parser.addOption('mode');
+var results = parser.parse(['--mode', 'on', '--mode', 'off']);
+print(results.option('mode')); // prints 'off'
+```
+
+Multiple values can be parsed with `addMultiOption()`. With this method, an
+option can occur multiple times, and the `parse()` method returns a list of
+values:
+
+```dart
+var parser = ArgParser();
+parser.addMultiOption('mode');
+var results = parser.parse(['--mode', 'on', '--mode', 'off']);
+print(results.multiOption('mode')); // prints '[on, off]'
+```
+
+By default, values for a multi-valued option may also be separated with commas:
+
+```dart
+var parser = ArgParser();
+parser.addMultiOption('mode');
+var results = parser.parse(['--mode', 'on,off']);
+print(results.multiOption('mode')); // prints '[on, off]'
+```
+
+This can be disabled by passing `splitCommas: false`.
+
+## Defining commands
+
+In addition to *options*, you can also define *commands*. A command is a named
+argument that has its own set of options. For example, consider this shell
+command:
+
+```
+$ git commit -a
+```
+
+The executable is `git`, the command is `commit`, and the `-a` option is an
+option passed to the command. You can add a command using the [addCommand][]
+method:
+
+```dart
+var parser = ArgParser();
+var command = parser.addCommand('commit');
+```
+
+It returns another [ArgParser][], which you can then use to define options
+specific to that command. If you already have an [ArgParser][] for the command's
+options, you can pass it in:
+
+```dart
+var parser = ArgParser();
+var command = ArgParser();
+parser.addCommand('commit', command);
+```
+
+The [ArgParser][] for a command can then define options or flags:
+
+```dart
+command.addFlag('all', abbr: 'a');
+```
+
+You can add multiple commands to the same parser so that a user can select one
+from a range of possible commands. When parsing an argument list, you can then
+determine which command was entered and what options were provided for it.
+
+```dart
+var results = parser.parse(['commit', '-a']);
+print(results.command.name); // "commit"
+print(results.command['all']); // true
+```
+
+Options for a command must appear after the command in the argument list. For
+example, given the above parser, `"git -a commit"` is *not* valid. The parser
+tries to find the right-most command that accepts an option. For example:
+
+```dart
+var parser = ArgParser();
+parser.addFlag('all', abbr: 'a');
+var command = parser.addCommand('commit');
+command.addFlag('all', abbr: 'a');
+
+var results = parser.parse(['commit', '-a']);
+print(results.command['all']); // true
+```
+
+Here, both the top-level parser and the `"commit"` command can accept a `"-a"`
+(which is probably a bad command line interface, admittedly). In that case, when
+`"-a"` appears after `"commit"`, it is applied to that command. If it appears to
+the left of `"commit"`, it is given to the top-level parser.
+
+## Dispatching Commands
+
+If you're writing a command-based application, you can use the [CommandRunner][]
+and [Command][] classes to help structure it. [CommandRunner][] has built-in
+support for dispatching to [Command][]s based on command-line arguments, as well
+as handling `--help` flags and invalid arguments.
+
+When using the [CommandRunner][] it replaces the [ArgParser][].
+
+In the following example we build a dart application called `dgit` that takes commands `commit` and `stash`.
+
+The [CommandRunner][] takes an `executableName` which is used to generate the help message.
+
+e.g.
+`dgit commit -a`
+
+File `dgit.dart`
+
+```dart
+void main(List<String> args) {
+ var runner = CommandRunner("dgit", "A dart implementation of distributed version control.")
+ ..addCommand(CommitCommand())
+ ..addCommand(StashCommand())
+ ..run(args);
+}
+```
+
+When the above `run(args)` line executes it parses the command line args looking for one of the commands (`commit` or `stash`).
+
+If the [CommandRunner][] finds a matching command then the [CommandRunner][] calls the overridden `run()` method on the matching command (e.g. CommitCommand().run).
+
+Commands are defined by extending the [Command][] class. For example:
+
+```dart
+class CommitCommand extends Command {
+ // The [name] and [description] properties must be defined by every
+ // subclass.
+ final name = "commit";
+ final description = "Record changes to the repository.";
+
+ CommitCommand() {
+ // we can add command specific arguments here.
+ // [argParser] is automatically created by the parent class.
+ argParser.addFlag('all', abbr: 'a');
+ }
+
+ // [run] may also return a Future.
+ void run() {
+ // [argResults] is set before [run()] is called and contains the flags/options
+ // passed to this command.
+ print(argResults.flag('all'));
+ }
+}
+```
+
+### CommandRunner Arguments
+The [CommandRunner][] allows you to specify both global args as well as command specific arguments (and even sub-command specific arguments).
+
+#### Global Arguments
+Add argments directly to the [CommandRunner] to specify global arguments:
+
+Adding global arguments
+
+```dart
+var runner = CommandRunner('dgit', "A dart implementation of distributed version control.");
+// add global flag
+runner.argParser.addFlag('verbose', abbr: 'v', help: 'increase logging');
+```
+
+#### Command specific Arguments
+Add arguments to each [Command][] to specify [Command][] specific arguments.
+
+```dart
+ CommitCommand() {
+ // we can add command specific arguments here.
+ // [argParser] is automatically created by the parent class.
+ argParser.addFlag('all', abbr: 'a');
+ }
+```
+
+### SubCommands
+
+Commands can also have subcommands, which are added with [addSubcommand][]. A
+command with subcommands can't run its own code, so [run][] doesn't need to be
+implemented. For example:
+
+```dart
+class StashCommand extends Command {
+ final String name = "stash";
+ final String description = "Stash changes in the working directory.";
+
+ StashCommand() {
+ addSubcommand(StashSaveCommand());
+ addSubcommand(StashListCommand());
+ }
+}
+```
+
+### Default Help Command
+
+[CommandRunner][] automatically adds a `help` command that displays usage
+information for commands, as well as support for the `--help` flag for all
+commands. If it encounters an error parsing the arguments or processing a
+command, it throws a [UsageException][]; your `main()` method should catch these and
+print them appropriately. For example:
+
+```dart
+runner.run(arguments).catchError((error) {
+ if (error is! UsageException) throw error;
+ print(error);
+ exit(64); // Exit code 64 indicates a usage error.
+});
+```
+
+## Displaying usage
+
+You can automatically generate nice help text, suitable for use as the output of
+`--help`. To display good usage information, you should provide some help text
+when you create your options.
+
+To define help text for an entire option, use the `help:` parameter:
+
+```dart
+parser.addOption('mode', help: 'The compiler configuration',
+ allowed: ['debug', 'release']);
+parser.addFlag('verbose', help: 'Show additional diagnostic info');
+```
+
+For non-flag options, you can also provide a help string for the parameter:
+
+```dart
+parser.addOption('out', help: 'The output path', valueHelp: 'path',
+ allowed: ['debug', 'release']);
+```
+
+For non-flag options, you can also provide detailed help for each expected value
+by using the `allowedHelp:` parameter:
+
+```dart
+parser.addOption('arch', help: 'The architecture to compile for',
+ allowedHelp: {
+ 'ia32': 'Intel x86',
+ 'arm': 'ARM Holding 32-bit chip'
+ });
+```
+
+To display the help, use the [usage][usage] getter:
+
+```dart
+print(parser.usage);
+```
+
+The resulting string looks something like this:
+
+```
+--mode The compiler configuration
+ [debug, release]
+
+--out=<path> The output path
+--[no-]verbose Show additional diagnostic info
+--arch The architecture to compile for
+ [arm] ARM Holding 32-bit chip
+ [ia32] Intel x86
+```
+
+[posix]: https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02
+[gnu]: https://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces
+[ArgParser]: https://pub.dev/documentation/args/latest/args/ArgParser/ArgParser.html
+[ArgParserException]: https://pub.dev/documentation/args/latest/args/ArgParserException-class.html
+[ArgResults]: https://pub.dev/documentation/args/latest/args/ArgResults-class.html
+[CommandRunner]: https://pub.dev/documentation/args/latest/command_runner/CommandRunner-class.html
+[Command]: https://pub.dev/documentation/args/latest/command_runner/Command-class.html
+[UsageException]: https://pub.dev/documentation/args/latest/command_runner/UsageException-class.html
+[addOption]: https://pub.dev/documentation/args/latest/args/ArgParser/addOption.html
+[addFlag]: https://pub.dev/documentation/args/latest/args/ArgParser/addFlag.html
+[parse]: https://pub.dev/documentation/args/latest/args/ArgParser/parse.html
+[rest]: https://pub.dev/documentation/args/latest/args/ArgResults/rest.html
+[addCommand]: https://pub.dev/documentation/args/latest/args/ArgParser/addCommand.html
+[usage]: https://pub.dev/documentation/args/latest/args/ArgParser/usage.html
+[addSubcommand]: https://pub.dev/documentation/args/latest/command_runner/Command/addSubcommand.html
+[run]: https://pub.dev/documentation/args/latest/command_runner/CommandRunner/run.html
diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml
new file mode 100644
index 0000000..a96c5ee
--- /dev/null
+++ b/pkgs/args/analysis_options.yaml
@@ -0,0 +1,14 @@
+# https://dart.dev/guides/language/analysis-options
+
+include: package:dart_flutter_team_lints/analysis_options.yaml
+
+linter:
+ rules:
+ - avoid_unused_constructor_parameters
+ - cancel_subscriptions
+ - literal_only_boolean_expressions
+ - missing_whitespace_between_adjacent_strings
+ - no_adjacent_strings_in_list
+ - no_runtimeType_toString
+ - package_api_docs
+ - unnecessary_await_in_return
diff --git a/pkgs/args/example/arg_parser/README.md b/pkgs/args/example/arg_parser/README.md
new file mode 100644
index 0000000..c137bcd
--- /dev/null
+++ b/pkgs/args/example/arg_parser/README.md
@@ -0,0 +1,3 @@
+# Example of using `ArgParser`
+
+`dart run example.dart`
diff --git a/pkgs/args/example/arg_parser/example.dart b/pkgs/args/example/arg_parser/example.dart
new file mode 100644
index 0000000..0a6cc97
--- /dev/null
+++ b/pkgs/args/example/arg_parser/example.dart
@@ -0,0 +1,169 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// This is an example of converting the args in test.dart to use this API.
+/// It shows what it looks like to build an [ArgParser] and then, when the code
+/// is run, demonstrates what the generated usage text looks like.
+library;
+
+import 'dart:io';
+
+import 'package:args/args.dart';
+
+void main() {
+ var parser = ArgParser();
+
+ parser.addSeparator('===== Platform');
+
+ parser.addOption('compiler',
+ abbr: 'c',
+ defaultsTo: 'none',
+ help: 'Specify any compilation step (if needed).',
+ allowed: [
+ 'none',
+ 'dart2js',
+ 'dartc'
+ ],
+ allowedHelp: {
+ 'none': 'Do not compile the Dart code (run native Dart code on the'
+ ' VM).\n(only valid with the following runtimes: vm, drt)',
+ 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n'
+ '(only valid with the following runtimes: d8, drt, chrome\n'
+ 'safari, ie, firefox, opera, none (compile only))',
+ 'dartc': 'Perform static analysis on Dart code by running dartc.\n'
+ '(only valid with the following runtimes: none)',
+ });
+
+ parser.addOption('runtime',
+ abbr: 'r',
+ defaultsTo: 'vm',
+ help: 'Where the tests should be run.',
+ allowed: [
+ 'vm',
+ 'd8',
+ 'drt',
+ 'dartium',
+ 'ff',
+ 'firefox',
+ 'chrome',
+ 'safari',
+ 'ie',
+ 'opera',
+ 'none'
+ ],
+ allowedHelp: {
+ 'vm': 'Run Dart code on the standalone dart vm.',
+ 'd8': 'Run JavaScript from the command line using v8.',
+ 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n'
+ 'content shell.',
+ 'dartium': 'Run Dart or JavaScript in Dartium.',
+ 'ff': 'Run JavaScript in Firefox',
+ 'chrome': 'Run JavaScript in Chrome',
+ 'safari': 'Run JavaScript in Safari',
+ 'ie': 'Run JavaScript in Internet Explorer',
+ 'opera': 'Run JavaScript in Opera',
+ 'none': 'No runtime, compile only (for example, used for dartc static\n'
+ 'analysis tests).',
+ });
+
+ parser.addOption('arch',
+ abbr: 'a',
+ defaultsTo: 'ia32',
+ help: 'The architecture to run tests for',
+ allowed: ['all', 'ia32', 'x64', 'simarm']);
+
+ parser.addOption('system',
+ abbr: 's',
+ defaultsTo: Platform.operatingSystem,
+ help: 'The operating system to run tests on',
+ allowed: ['linux', 'macos', 'windows']);
+
+ parser.addSeparator('===== Runtime');
+
+ parser.addOption('mode',
+ abbr: 'm',
+ defaultsTo: 'debug',
+ help: 'Mode in which to run the tests',
+ allowed: ['all', 'debug', 'release']);
+
+ parser.addFlag('checked',
+ defaultsTo: false, help: 'Run tests in checked mode');
+
+ parser.addFlag('host-checked',
+ defaultsTo: false, help: 'Run compiler in checked mode');
+
+ parser.addOption('timeout', abbr: 't', help: 'Timeout in seconds');
+
+ parser.addOption('tasks',
+ abbr: 'j',
+ defaultsTo: Platform.numberOfProcessors.toString(),
+ help: 'The number of parallel tasks to run');
+
+ parser.addOption('shards',
+ defaultsTo: '1',
+ help: 'The number of instances that the tests will be sharded over');
+
+ parser.addOption('shard',
+ defaultsTo: '1',
+ help: 'The index of this instance when running in sharded mode');
+
+ parser.addFlag('valgrind',
+ defaultsTo: false, help: 'Run tests through valgrind');
+
+ parser.addSeparator('===== Output');
+
+ parser.addOption('progress',
+ abbr: 'p',
+ defaultsTo: 'compact',
+ help: 'Progress indication mode',
+ allowed: [
+ 'compact',
+ 'color',
+ 'line',
+ 'verbose',
+ 'silent',
+ 'status',
+ 'buildbot'
+ ]);
+
+ parser.addFlag('report',
+ defaultsTo: false,
+ help: 'Print a summary report of the number of tests, by expectation');
+
+ parser.addFlag('verbose',
+ abbr: 'v', defaultsTo: false, help: 'Verbose output');
+
+ parser.addFlag('list',
+ defaultsTo: false, help: 'List tests only, do not run them');
+
+ parser.addFlag('time',
+ help: 'Print timing information after running tests', defaultsTo: false);
+
+ parser.addFlag('batch',
+ abbr: 'b', help: 'Run browser tests in batch mode', defaultsTo: true);
+
+ parser.addSeparator('===== Miscellaneous');
+
+ parser.addFlag('keep-generated-tests',
+ defaultsTo: false,
+ help: 'Keep the generated files in the temporary directory');
+
+ parser.addOption('special-command', help: """
+Special command support. Wraps the command line in
+a special command. The special command should contain
+an '@' character which will be replaced by the normal
+command.
+
+For example if the normal command that will be executed
+is 'dart file.dart' and you specify special command
+'python -u valgrind.py @ suffix' the final command will be
+'python -u valgrind.py dart file.dart suffix'""");
+
+ parser.addOption('dart', help: 'Path to dart executable');
+ parser.addOption('drt', help: 'Path to content shell executable');
+ parser.addOption('dartium', help: 'Path to Dartium Chrome executable');
+ parser.addOption('mandatory', help: 'A mandatory option', mandatory: true);
+
+ print(parser.usage);
+}
diff --git a/pkgs/args/example/arg_parser/pubspec.yaml b/pkgs/args/example/arg_parser/pubspec.yaml
new file mode 100644
index 0000000..52f193a
--- /dev/null
+++ b/pkgs/args/example/arg_parser/pubspec.yaml
@@ -0,0 +1,13 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+name: arg_parser_example
+version: 1.0.0
+description: An example of using ArgParser
+publish_to: 'none'
+environment:
+ sdk: '>=2.14.0 <3.0.0'
+dependencies:
+ args:
+ path: ../..
diff --git a/pkgs/args/example/command_runner/README.md b/pkgs/args/example/command_runner/README.md
new file mode 100644
index 0000000..48bf60e
--- /dev/null
+++ b/pkgs/args/example/command_runner/README.md
@@ -0,0 +1,5 @@
+# Example of using `CommandRunner`
+
+This example uses `CommandRunner` to create a tool for drawing ascii art shapes.
+
+`dart run draw.dart circle --radius=10`
diff --git a/pkgs/args/example/command_runner/draw.dart b/pkgs/args/example/command_runner/draw.dart
new file mode 100644
index 0000000..018bf59
--- /dev/null
+++ b/pkgs/args/example/command_runner/draw.dart
@@ -0,0 +1,142 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:math';
+
+import 'package:args/command_runner.dart';
+
+void main(List<String> args) async {
+ final runner = CommandRunner<String>('draw', 'Draws shapes')
+ ..addCommand(SquareCommand())
+ ..addCommand(CircleCommand())
+ ..addCommand(TriangleCommand());
+ runner.argParser.addOption('char', help: 'The character to use for drawing');
+ final output = await runner.run(args);
+ print(output);
+}
+
+class SquareCommand extends Command<String> {
+ SquareCommand() {
+ argParser.addOption('size', help: 'Size of the square');
+ }
+
+ @override
+ String get name => 'square';
+
+ @override
+ String get description => 'Draws a square';
+
+ @override
+ List<String> get aliases => ['s'];
+
+ @override
+ FutureOr<String>? run() {
+ final size = int.parse(argResults?.option('size') ?? '20');
+ final char = globalResults?.option('char')?[0] ?? '#';
+ return draw(size, size, char, (x, y) => true);
+ }
+}
+
+class CircleCommand extends Command<String> {
+ CircleCommand() {
+ argParser.addOption('radius', help: 'Radius of the circle');
+ }
+
+ @override
+ String get name => 'circle';
+
+ @override
+ String get description => 'Draws a circle';
+
+ @override
+ List<String> get aliases => ['c'];
+
+ @override
+ FutureOr<String>? run() {
+ final size = 2 * int.parse(argResults?.option('radius') ?? '10');
+ final char = globalResults?.option('char')?[0] ?? '#';
+ return draw(size, size, char, (x, y) => x * x + y * y < 1);
+ }
+}
+
+class TriangleCommand extends Command<String> {
+ TriangleCommand() {
+ addSubcommand(EquilateralTriangleCommand());
+ addSubcommand(IsoscelesTriangleCommand());
+ }
+
+ @override
+ String get name => 'triangle';
+
+ @override
+ String get description => 'Draws a triangle';
+
+ @override
+ List<String> get aliases => ['t'];
+}
+
+class EquilateralTriangleCommand extends Command<String> {
+ EquilateralTriangleCommand() {
+ argParser.addOption('size', help: 'Size of the triangle');
+ }
+
+ @override
+ String get name => 'equilateral';
+
+ @override
+ String get description => 'Draws an equilateral triangle';
+
+ @override
+ List<String> get aliases => ['e'];
+
+ @override
+ FutureOr<String>? run() {
+ final size = int.parse(argResults?.option('size') ?? '20');
+ final char = globalResults?.option('char')?[0] ?? '#';
+ return drawTriangle(size, size * sqrt(3) ~/ 2, char);
+ }
+}
+
+class IsoscelesTriangleCommand extends Command<String> {
+ IsoscelesTriangleCommand() {
+ argParser.addOption('width', help: 'Width of the triangle');
+ argParser.addOption('height', help: 'Height of the triangle');
+ }
+
+ @override
+ String get name => 'isosceles';
+
+ @override
+ String get description => 'Draws an isosceles triangle';
+
+ @override
+ List<String> get aliases => ['i'];
+
+ @override
+ FutureOr<String>? run() {
+ final width = int.parse(argResults?.option('width') ?? '50');
+ final height = int.parse(argResults?.option('height') ?? '10');
+ final char = globalResults?.option('char')?[0] ?? '#';
+ return drawTriangle(width, height, char);
+ }
+}
+
+String draw(
+ int width, int height, String char, bool Function(double, double) pixel) {
+ final out = StringBuffer();
+ for (var y = 0; y <= height; ++y) {
+ final ty = 2 * y / height - 1;
+ for (var x = 0; x <= width; ++x) {
+ final tx = 2 * x / width - 1;
+ out.write(pixel(tx, ty) ? char : ' ');
+ }
+ out.write('\n');
+ }
+ return out.toString();
+}
+
+String drawTriangle(int width, int height, String char) {
+ return draw(width, height, char, (x, y) => x.abs() <= (1 + y) / 2);
+}
diff --git a/pkgs/args/example/command_runner/pubspec.yaml b/pkgs/args/example/command_runner/pubspec.yaml
new file mode 100644
index 0000000..0745be6
--- /dev/null
+++ b/pkgs/args/example/command_runner/pubspec.yaml
@@ -0,0 +1,13 @@
+# Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+name: command_runner_example
+version: 1.0.0
+description: An example of using CommandRunner
+publish_to: 'none'
+environment:
+ sdk: '>=2.14.0 <3.0.0'
+dependencies:
+ args:
+ path: ../..
diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart
new file mode 100644
index 0000000..6011d1e
--- /dev/null
+++ b/pkgs/args/lib/args.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+export 'src/arg_parser.dart' show ArgParser;
+export 'src/arg_parser_exception.dart' show ArgParserException;
+export 'src/arg_results.dart' show ArgResults;
+export 'src/option.dart' show Option, OptionType;
diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart
new file mode 100644
index 0000000..e72a08d
--- /dev/null
+++ b/pkgs/args/lib/command_runner.dart
@@ -0,0 +1,559 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'src/arg_parser.dart';
+import 'src/arg_parser_exception.dart';
+import 'src/arg_results.dart';
+import 'src/help_command.dart';
+import 'src/usage_exception.dart';
+import 'src/utils.dart';
+
+export 'src/usage_exception.dart';
+
+/// A class for invoking [Command]s based on raw command-line arguments.
+///
+/// The type argument `T` represents the type returned by [Command.run] and
+/// [CommandRunner.run]; it can be ommitted if you're not using the return
+/// values.
+class CommandRunner<T> {
+ /// The name of the executable being run.
+ ///
+ /// Used for error reporting and [usage].
+ final String executableName;
+
+ /// A short description of this executable.
+ final String description;
+
+ /// A single-line template for how to invoke this executable.
+ ///
+ /// Defaults to `"$executableName <command> arguments`". Subclasses can
+ /// override this for a more specific template.
+ String get invocation => '$executableName <command> [arguments]';
+
+ /// Generates a string displaying usage information for the executable.
+ ///
+ /// This includes usage for the global arguments as well as a list of
+ /// top-level commands.
+ String get usage => _wrap('$description\n\n') + _usageWithoutDescription;
+
+ /// An optional footer for [usage].
+ ///
+ /// If a subclass overrides this to return a string, it will automatically be
+ /// added to the end of [usage].
+ String? get usageFooter => null;
+
+ /// Returns [usage] with [description] removed from the beginning.
+ String get _usageWithoutDescription {
+ var usagePrefix = 'Usage:';
+ var buffer = StringBuffer();
+ buffer.writeln(
+ '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n',
+ );
+ buffer.writeln(_wrap('Global options:'));
+ buffer.writeln('${argParser.usage}\n');
+ buffer.writeln(
+ '${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)}\n',
+ );
+ buffer.write(_wrap(
+ 'Run "$executableName help <command>" for more information about a '
+ 'command.'));
+ if (usageFooter != null) {
+ buffer.write('\n${_wrap(usageFooter!)}');
+ }
+ return buffer.toString();
+ }
+
+ /// An unmodifiable view of all top-level commands defined for this runner.
+ Map<String, Command<T>> get commands => UnmodifiableMapView(_commands);
+ final _commands = <String, Command<T>>{};
+
+ /// The top-level argument parser.
+ ///
+ /// Global options should be registered with this parser; they'll end up
+ /// available via [Command.globalResults]. Commands should be registered with
+ /// [addCommand] rather than directly on the parser.
+ ArgParser get argParser => _argParser;
+ final ArgParser _argParser;
+
+ /// The maximum edit distance allowed when suggesting possible intended
+ /// commands.
+ ///
+ /// Set to `0` in order to disable suggestions, defaults to `2`.
+ final int suggestionDistanceLimit;
+
+ CommandRunner(this.executableName, this.description,
+ {int? usageLineLength, this.suggestionDistanceLimit = 2})
+ : _argParser = ArgParser(usageLineLength: usageLineLength) {
+ argParser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Print this usage information.');
+ addCommand(HelpCommand<T>());
+ }
+
+ /// Prints the usage information for this runner.
+ ///
+ /// This is called internally by [run] and can be overridden by subclasses to
+ /// control how output is displayed or integrate with a logging system.
+ void printUsage() => print(usage);
+
+ /// Throws a [UsageException] with [message].
+ Never usageException(String message) =>
+ throw UsageException(message, _usageWithoutDescription);
+
+ /// Adds [Command] as a top-level command to this runner.
+ void addCommand(Command<T> command) {
+ var names = [command.name, ...command.aliases];
+ for (var name in names) {
+ _commands[name] = command;
+ argParser.addCommand(name, command.argParser);
+ }
+ command._runner = this;
+ }
+
+ /// Parses [args] and invokes [Command.run] on the chosen command.
+ ///
+ /// This always returns a [Future] in case the command is asynchronous. The
+ /// [Future] will throw a [UsageException] if [args] was invalid.
+ Future<T?> run(Iterable<String> args) =>
+ Future.sync(() => runCommand(parse(args)));
+
+ /// Parses [args] and returns the result, converting an [ArgParserException]
+ /// to a [UsageException].
+ ///
+ /// This is notionally a protected method. It may be overridden or called from
+ /// subclasses, but it shouldn't be called externally.
+ ArgResults parse(Iterable<String> args) {
+ try {
+ return argParser.parse(args);
+ } on ArgParserException catch (error) {
+ if (error.commands.isEmpty) usageException(error.message);
+
+ var command = commands[error.commands.first]!;
+ for (var commandName in error.commands.skip(1)) {
+ command = command.subcommands[commandName]!;
+ }
+
+ command.usageException(error.message);
+ }
+ }
+
+ /// Runs the command specified by [topLevelResults].
+ ///
+ /// This is notionally a protected method. It may be overridden or called from
+ /// subclasses, but it shouldn't be called externally.
+ ///
+ /// It's useful to override this to handle global flags and/or wrap the entire
+ /// command in a block. For example, you might handle the `--verbose` flag
+ /// here to enable verbose logging before running the command.
+ ///
+ /// This returns the return value of [Command.run].
+ Future<T?> runCommand(ArgResults topLevelResults) async {
+ var argResults = topLevelResults;
+ var commands = _commands;
+ Command? command;
+ var commandString = executableName;
+
+ while (commands.isNotEmpty) {
+ if (argResults.command == null) {
+ if (argResults.rest.isEmpty) {
+ if (command == null) {
+ // No top-level command was chosen.
+ printUsage();
+ return null;
+ }
+
+ command.usageException('Missing subcommand for "$commandString".');
+ } else {
+ var requested = argResults.rest[0];
+
+ // Build up a help message containing similar commands, if found.
+ var similarCommands =
+ _similarCommandsText(requested, commands.values);
+
+ if (command == null) {
+ usageException(
+ 'Could not find a command named "$requested".$similarCommands');
+ }
+
+ command.usageException('Could not find a subcommand named '
+ '"$requested" for "$commandString".$similarCommands');
+ }
+ }
+
+ // Step into the command.
+ argResults = argResults.command!;
+ command = commands[argResults.name]!;
+ command._globalResults = topLevelResults;
+ command._argResults = argResults;
+ commands = command._subcommands as Map<String, Command<T>>;
+ commandString += ' ${argResults.name}';
+
+ if (argResults.options.contains('help') && argResults.flag('help')) {
+ command.printUsage();
+ return null;
+ }
+ }
+
+ if (topLevelResults.flag('help')) {
+ command!.printUsage();
+ return null;
+ }
+
+ // Make sure there aren't unexpected arguments.
+ if (!command!.takesArguments && argResults.rest.isNotEmpty) {
+ command.usageException(
+ 'Command "${argResults.name}" does not take any arguments.');
+ }
+
+ return (await command.run()) as T?;
+ }
+
+ // Returns help text for commands similar to `name`, in sorted order.
+ String _similarCommandsText(String name, Iterable<Command<T>> commands) {
+ if (suggestionDistanceLimit <= 0) return '';
+ var distances = <Command<T>, int>{};
+ var candidates =
+ SplayTreeSet<Command<T>>((a, b) => distances[a]! - distances[b]!);
+ for (var command in commands) {
+ if (command.hidden) continue;
+ for (var alias in [
+ command.name,
+ ...command.aliases,
+ ...command.suggestionAliases
+ ]) {
+ var distance = _editDistance(name, alias);
+ if (distance <= suggestionDistanceLimit) {
+ distances[command] =
+ math.min(distances[command] ?? distance, distance);
+ candidates.add(command);
+ }
+ }
+ }
+ if (candidates.isEmpty) return '';
+
+ var similar = StringBuffer();
+ similar
+ ..writeln()
+ ..writeln()
+ ..writeln('Did you mean one of these?');
+ for (var command in candidates) {
+ similar.writeln(' ${command.name}');
+ }
+
+ return similar.toString();
+ }
+
+ String _wrap(String text, {int? hangingIndent}) => wrapText(text,
+ length: argParser.usageLineLength, hangingIndent: hangingIndent);
+}
+
+/// A single command.
+///
+/// A command is known as a "leaf command" if it has no subcommands and is meant
+/// to be run. Leaf commands must override [run].
+///
+/// A command with subcommands is known as a "branch command" and cannot be run
+/// itself. It should call [addSubcommand] (often from the constructor) to
+/// register subcommands.
+abstract class Command<T> {
+ /// The name of this command.
+ String get name;
+
+ /// A description of this command, included in [usage].
+ String get description;
+
+ /// A short description of this command, included in [parent]'s
+ /// [CommandRunner.usage].
+ ///
+ /// This defaults to the first line of [description].
+ String get summary => description.split('\n').first;
+
+ /// The command's category.
+ ///
+ /// Displayed in [parent]'s [CommandRunner.usage]. Commands with categories
+ /// will be grouped together, and displayed after commands without a category.
+ String get category => '';
+
+ /// A single-line template for how to invoke this command (e.g. `"pub get
+ /// `package`"`).
+ String get invocation {
+ var parents = [name];
+ for (var command = parent; command != null; command = command.parent) {
+ parents.add(command.name);
+ }
+ parents.add(runner!.executableName);
+
+ var invocation = parents.reversed.join(' ');
+ return _subcommands.isNotEmpty
+ ? '$invocation <subcommand> [arguments]'
+ : '$invocation [arguments]';
+ }
+
+ /// The command's parent command, if this is a subcommand.
+ ///
+ /// This will be `null` until [addSubcommand] has been called with
+ /// this command.
+ Command<T>? get parent => _parent;
+ Command<T>? _parent;
+
+ /// The command runner for this command.
+ ///
+ /// This will be `null` until [CommandRunner.addCommand] has been called with
+ /// this command or one of its parents.
+ CommandRunner<T>? get runner {
+ if (parent == null) return _runner;
+ return parent!.runner;
+ }
+
+ CommandRunner<T>? _runner;
+
+ /// The parsed global argument results.
+ ///
+ /// This will be `null` until just before [Command.run] is called.
+ ArgResults? get globalResults => _globalResults;
+ ArgResults? _globalResults;
+
+ /// The parsed argument results for this command.
+ ///
+ /// This will be `null` until just before [Command.run] is called.
+ ArgResults? get argResults => _argResults;
+ ArgResults? _argResults;
+
+ /// The argument parser for this command.
+ ///
+ /// Options for this command should be registered with this parser (often in
+ /// the constructor); they'll end up available via [argResults]. Subcommands
+ /// should be registered with [addSubcommand] rather than directly on the
+ /// parser.
+ ///
+ /// This can be overridden to change the arguments passed to the `ArgParser`
+ /// constructor.
+ ArgParser get argParser => _argParser;
+ final _argParser = ArgParser();
+
+ /// Generates a string displaying usage information for this command.
+ ///
+ /// This includes usage for the command's arguments as well as a list of
+ /// subcommands, if there are any.
+ String get usage => _wrap('$description\n\n') + _usageWithoutDescription;
+
+ /// An optional footer for [usage].
+ ///
+ /// If a subclass overrides this to return a string, it will automatically be
+ /// added to the end of [usage].
+ String? get usageFooter => null;
+
+ String _wrap(String text, {int? hangingIndent}) {
+ return wrapText(text,
+ length: argParser.usageLineLength, hangingIndent: hangingIndent);
+ }
+
+ /// Returns [usage] with [description] removed from the beginning.
+ String get _usageWithoutDescription {
+ var length = argParser.usageLineLength;
+ var usagePrefix = 'Usage: ';
+ var buffer = StringBuffer()
+ ..writeln(
+ usagePrefix + _wrap(invocation, hangingIndent: usagePrefix.length))
+ ..writeln(argParser.usage);
+
+ if (_subcommands.isNotEmpty) {
+ buffer.writeln();
+ buffer.writeln(_getCommandUsage(
+ _subcommands,
+ isSubcommand: true,
+ lineLength: length,
+ ));
+ }
+
+ buffer.writeln();
+ buffer.write(
+ _wrap('Run "${runner!.executableName} help" to see global options.'));
+
+ if (usageFooter != null) {
+ buffer.writeln();
+ buffer.write(_wrap(usageFooter!));
+ }
+
+ return buffer.toString();
+ }
+
+ /// An unmodifiable view of all sublevel commands of this command.
+ Map<String, Command<T>> get subcommands => UnmodifiableMapView(_subcommands);
+ final _subcommands = <String, Command<T>>{};
+
+ /// Whether or not this command should be hidden from help listings.
+ ///
+ /// This is intended to be overridden by commands that want to mark themselves
+ /// hidden.
+ ///
+ /// By default, leaf commands are always visible. Branch commands are visible
+ /// as long as any of their leaf commands are visible.
+ bool get hidden {
+ // Leaf commands are visible by default.
+ if (_subcommands.isEmpty) return false;
+
+ // Otherwise, a command is hidden if all of its subcommands are.
+ return _subcommands.values.every((subcommand) => subcommand.hidden);
+ }
+
+ /// Whether or not this command takes positional arguments in addition to
+ /// options.
+ ///
+ /// If false, [CommandRunner.run] will throw a [UsageException] if arguments
+ /// are provided. Defaults to true.
+ ///
+ /// This is intended to be overridden by commands that don't want to receive
+ /// arguments. It has no effect for branch commands.
+ bool get takesArguments => true;
+
+ /// Alternate names for this command.
+ ///
+ /// These names won't be used in the documentation, but they will work when
+ /// invoked on the command line.
+ ///
+ /// This is intended to be overridden.
+ List<String> get aliases => const [];
+
+ /// Alternate non-functional names for this command.
+ ///
+ /// These names won't be used in the documentation, and also they won't work
+ /// when invoked on the command line. But if an unknown command is used it
+ /// will be matched against this when creating suggestions.
+ ///
+ /// A name does not have to be repeated both here and in [aliases].
+ ///
+ /// This is intended to be overridden.
+ List<String> get suggestionAliases => const [];
+
+ Command() {
+ if (!argParser.allowsAnything) {
+ argParser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Print this usage information.');
+ }
+ }
+
+ /// Runs this command.
+ ///
+ /// The return value is wrapped in a `Future` if necessary and returned by
+ /// [CommandRunner.runCommand].
+ FutureOr<T>? run() {
+ throw UnimplementedError(_wrap('Leaf command $this must implement run().'));
+ }
+
+ /// Adds [Command] as a subcommand of this.
+ void addSubcommand(Command<T> command) {
+ var names = [command.name, ...command.aliases];
+ for (var name in names) {
+ _subcommands[name] = command;
+ argParser.addCommand(name, command.argParser);
+ }
+ command._parent = this;
+ }
+
+ /// Prints the usage information for this command.
+ ///
+ /// This is called internally by [run] and can be overridden by subclasses to
+ /// control how output is displayed or integrate with a logging system.
+ void printUsage() => print(usage);
+
+ /// Throws a [UsageException] with [message].
+ Never usageException(String message) =>
+ throw UsageException(_wrap(message), _usageWithoutDescription);
+}
+
+/// Returns a string representation of [commands] fit for use in a usage string.
+///
+/// [isSubcommand] indicates whether the commands should be called "commands" or
+/// "subcommands".
+String _getCommandUsage(Map<String, Command> commands,
+ {bool isSubcommand = false, int? lineLength}) {
+ // Don't include aliases.
+ var names =
+ commands.keys.where((name) => !commands[name]!.aliases.contains(name));
+
+ // Filter out hidden ones, unless they are all hidden.
+ var visible = names.where((name) => !commands[name]!.hidden);
+ if (visible.isNotEmpty) names = visible;
+
+ // Show the commands alphabetically.
+ names = names.toList()..sort();
+
+ // Group the commands by category.
+ var commandsByCategory = SplayTreeMap<String, List<Command>>();
+ for (var name in names) {
+ var category = commands[name]!.category;
+ commandsByCategory.putIfAbsent(category, () => []).add(commands[name]!);
+ }
+ final categories = commandsByCategory.keys.toList();
+
+ var length = names.map((name) => name.length).reduce(math.max);
+
+ var buffer = StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:');
+ var columnStart = length + 5;
+ for (var category in categories) {
+ if (category != '') {
+ buffer.writeln();
+ buffer.writeln();
+ buffer.write(category);
+ }
+ for (var command in commandsByCategory[category]!) {
+ var lines = wrapTextAsLines(command.summary,
+ start: columnStart, length: lineLength);
+ buffer.writeln();
+ buffer.write(' ${padRight(command.name, length)} ${lines.first}');
+
+ for (var line in lines.skip(1)) {
+ buffer.writeln();
+ buffer.write(' ' * columnStart);
+ buffer.write(line);
+ }
+ }
+ }
+
+ return buffer.toString();
+}
+
+/// Returns the edit distance between `from` and `to`.
+//
+/// Allows for edits, deletes, substitutions, and swaps all as single cost.
+///
+/// See https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance#Optimal_string_alignment_distance
+int _editDistance(String from, String to) {
+ // Add a space in front to mimic indexing by 1 instead of 0.
+ from = ' $from';
+ to = ' $to';
+ var distances = [
+ for (var i = 0; i < from.length; i++)
+ [
+ for (var j = 0; j < to.length; j++)
+ if (i == 0) j else if (j == 0) i else 0,
+ ],
+ ];
+
+ for (var i = 1; i < from.length; i++) {
+ for (var j = 1; j < to.length; j++) {
+ // Removals from `from`.
+ var min = distances[i - 1][j] + 1;
+ // Additions to `from`.
+ min = math.min(min, distances[i][j - 1] + 1);
+ // Substitutions (and equality).
+ min = math.min(
+ min,
+ distances[i - 1][j - 1] +
+ // Cost is zero if substitution was not actually necessary.
+ (from[i] == to[j] ? 0 : 1));
+ // Allows for basic swaps, but no additional edits of swapped regions.
+ if (i > 1 && j > 1 && from[i] == to[j - 1] && from[i - 1] == to[j]) {
+ min = math.min(min, distances[i - 2][j - 2] + 1);
+ }
+ distances[i][j] = min;
+ }
+ }
+
+ return distances.last.last;
+}
diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart
new file mode 100644
index 0000000..46dfc94
--- /dev/null
+++ b/pkgs/args/lib/src/allow_anything_parser.dart
@@ -0,0 +1,106 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'arg_parser.dart';
+import 'arg_results.dart';
+import 'option.dart';
+import 'parser.dart';
+
+/// An ArgParser that treats *all input* as non-option arguments.
+class AllowAnythingParser implements ArgParser {
+ @override
+ Map<String, Option> get options => const {};
+ @override
+ Map<String, ArgParser> get commands => const {};
+ @override
+ bool get allowTrailingOptions => false;
+ @override
+ bool get allowsAnything => true;
+ @override
+ int? get usageLineLength => null;
+
+ @override
+ ArgParser addCommand(String name, [ArgParser? parser]) {
+ throw UnsupportedError(
+ "ArgParser.allowAnything().addCommands() isn't supported.");
+ }
+
+ @override
+ void addFlag(String name,
+ {String? abbr,
+ String? help,
+ bool? defaultsTo = false,
+ bool negatable = true,
+ void Function(bool)? callback,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ throw UnsupportedError(
+ "ArgParser.allowAnything().addFlag() isn't supported.");
+ }
+
+ @override
+ void addOption(String name,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ String? defaultsTo,
+ void Function(String?)? callback,
+ bool allowMultiple = false,
+ bool? splitCommas,
+ bool mandatory = false,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ throw UnsupportedError(
+ "ArgParser.allowAnything().addOption() isn't supported.");
+ }
+
+ @override
+ void addMultiOption(String name,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Iterable<String>? defaultsTo,
+ void Function(List<String>)? callback,
+ bool splitCommas = true,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ throw UnsupportedError(
+ "ArgParser.allowAnything().addMultiOption() isn't supported.");
+ }
+
+ @override
+ void addSeparator(String text) {
+ throw UnsupportedError(
+ "ArgParser.allowAnything().addSeparator() isn't supported.");
+ }
+
+ @override
+ ArgResults parse(Iterable<String> args) =>
+ Parser(null, this, Queue.of(args)).parse();
+
+ @override
+ String get usage => '';
+
+ @override
+ dynamic defaultFor(String option) {
+ throw ArgumentError('No option named $option');
+ }
+
+ @override
+ dynamic getDefault(String option) {
+ throw ArgumentError('No option named $option');
+ }
+
+ @override
+ Option? findByAbbreviation(String abbr) => null;
+
+ @override
+ Option? findByNameOrAlias(String name) => null;
+}
diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart
new file mode 100644
index 0000000..c47c003
--- /dev/null
+++ b/pkgs/args/lib/src/arg_parser.dart
@@ -0,0 +1,367 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'allow_anything_parser.dart';
+import 'arg_results.dart';
+import 'option.dart';
+import 'parser.dart';
+import 'usage.dart';
+
+/// A class for taking a list of raw command line arguments and parsing out
+/// options and flags from them.
+class ArgParser {
+ final Map<String, Option> _options;
+ final Map<String, ArgParser> _commands;
+
+ /// A map of aliases to the option names they alias.
+ final Map<String, String> _aliases;
+
+ /// The options that have been defined for this parser.
+ final Map<String, Option> options;
+
+ /// The commands that have been defined for this parser.
+ final Map<String, ArgParser> commands;
+
+ /// A list of the [Option]s in [options] intermingled with [String]
+ /// separators.
+ final _optionsAndSeparators = <Object>[];
+
+ /// Whether or not this parser parses options that appear after non-option
+ /// arguments.
+ final bool allowTrailingOptions;
+
+ /// An optional maximum line length for [usage] messages.
+ ///
+ /// If specified, then help messages in the usage are wrapped at the given
+ /// column, after taking into account the width of the options. Will refuse to
+ /// wrap help text to less than 10 characters of help text per line if there
+ /// isn't enough space on the line. It preserves embedded newlines, and
+ /// attempts to wrap at whitespace breaks (although it will split words if
+ /// there is no whitespace at which to split).
+ ///
+ /// If null (the default), help messages are not wrapped.
+ final int? usageLineLength;
+
+ /// Whether or not this parser treats unrecognized options as non-option
+ /// arguments.
+ bool get allowsAnything => false;
+
+ /// Creates a new ArgParser.
+ ///
+ /// If [allowTrailingOptions] is `true` (the default), the parser will parse
+ /// flags and options that appear after positional arguments. If it's `false`,
+ /// the parser stops parsing as soon as it finds an argument that is neither
+ /// an option nor a command.
+ factory ArgParser({bool allowTrailingOptions = true, int? usageLineLength}) =>
+ ArgParser._(<String, Option>{}, <String, ArgParser>{}, <String, String>{},
+ allowTrailingOptions: allowTrailingOptions,
+ usageLineLength: usageLineLength);
+
+ /// Creates a new ArgParser that treats *all input* as non-option arguments.
+ ///
+ /// This is intended to allow arguments to be passed through to child
+ /// processes without needing to be redefined in the parent.
+ ///
+ /// Options may not be defined for this parser.
+ factory ArgParser.allowAnything() = AllowAnythingParser;
+
+ ArgParser._(Map<String, Option> options, Map<String, ArgParser> commands,
+ this._aliases,
+ {this.allowTrailingOptions = true, this.usageLineLength})
+ : _options = options,
+ options = UnmodifiableMapView(options),
+ _commands = commands,
+ commands = UnmodifiableMapView(commands);
+
+ /// Defines a command.
+ ///
+ /// A command is a named argument which may in turn define its own options and
+ /// subcommands using the given parser. If [parser] is omitted, implicitly
+ /// creates a new one. Returns the parser for the command.
+ ///
+ /// Note that adding commands this way will not impact the [usage] string. To
+ /// add commands which are included in the usage string see `CommandRunner`.
+ ArgParser addCommand(String name, [ArgParser? parser]) {
+ // Make sure the name isn't in use.
+ if (_commands.containsKey(name)) {
+ throw ArgumentError('Duplicate command "$name".');
+ }
+
+ parser ??= ArgParser();
+ _commands[name] = parser;
+ return parser;
+ }
+
+ /// Defines a boolean flag.
+ ///
+ /// This adds an [Option] with the given properties to [options].
+ ///
+ /// The [abbr] argument is a single-character string that can be used as a
+ /// shorthand for this flag. For example, `abbr: "a"` will allow the user to
+ /// pass `-a` to enable the flag.
+ ///
+ /// The [help] argument is used by [usage] to describe this flag.
+ ///
+ /// The [defaultsTo] argument indicates the value this flag will have if the
+ /// user doesn't explicitly pass it in.
+ ///
+ /// The [negatable] argument indicates whether this flag's value can be set to
+ /// `false`. For example, if [name] is `flag`, the user can pass `--no-flag`
+ /// to set its value to `false`.
+ ///
+ /// The [callback] argument is invoked with the flag's value when the flag
+ /// is parsed. Note that this makes argument parsing order-dependent in ways
+ /// that are often surprising, and its use is discouraged in favor of reading
+ /// values from the [ArgResults].
+ ///
+ /// If [hide] is `true`, this option won't be included in [usage].
+ ///
+ /// If [aliases] is provided, these are used as aliases for [name]. These
+ /// aliases will not appear as keys in the [options] map.
+ ///
+ /// Throws an [ArgumentError] if:
+ ///
+ /// * There is already an option named [name].
+ /// * There is already an option using abbreviation [abbr].
+ void addFlag(String name,
+ {String? abbr,
+ String? help,
+ bool? defaultsTo = false,
+ bool negatable = true,
+ void Function(bool)? callback,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ _addOption(
+ name,
+ abbr,
+ help,
+ null,
+ null,
+ null,
+ defaultsTo,
+ callback == null ? null : (bool value) => callback(value),
+ OptionType.flag,
+ negatable: negatable,
+ hide: hide,
+ aliases: aliases);
+ }
+
+ /// Defines an option that takes a value.
+ ///
+ /// This adds an [Option] with the given properties to [options].
+ ///
+ /// The [abbr] argument is a single-character string that can be used as a
+ /// shorthand for this option. For example, `abbr: "a"` will allow the user to
+ /// pass `-a value` or `-avalue`.
+ ///
+ /// The [help] argument is used by [usage] to describe this option.
+ ///
+ /// The [valueHelp] argument is used by [usage] as a name for the value this
+ /// option takes. For example, `valueHelp: "FOO"` will include
+ /// `--option=<FOO>` rather than just `--option` in the usage string.
+ ///
+ /// The [allowed] argument is a list of valid values for this option. If
+ /// it's non-`null` and the user passes a value that's not included in the
+ /// list, [parse] will throw a [FormatException]. The allowed values will also
+ /// be included in [usage].
+ ///
+ /// The [allowedHelp] argument is a map from values in [allowed] to
+ /// documentation for those values that will be included in [usage].
+ ///
+ /// The [defaultsTo] argument indicates the value this option will have if the
+ /// user doesn't explicitly pass it in (or `null` by default).
+ ///
+ /// The [callback] argument is invoked with the option's value when the option
+ /// is parsed, or with `null` if the option was not parsed.
+ /// Note that this makes argument parsing order-dependent in ways that are
+ /// often surprising, and its use is discouraged in favor of reading values
+ /// from the [ArgResults].
+ ///
+ /// If [hide] is `true`, this option won't be included in [usage].
+ ///
+ /// If [aliases] is provided, these are used as aliases for [name]. These
+ /// aliases will not appear as keys in the [options] map.
+ ///
+ /// Throws an [ArgumentError] if:
+ ///
+ /// * There is already an option with name [name].
+ /// * There is already an option using abbreviation [abbr].
+ void addOption(String name,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ String? defaultsTo,
+ void Function(String?)? callback,
+ bool mandatory = false,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo,
+ callback, OptionType.single,
+ mandatory: mandatory, hide: hide, aliases: aliases);
+ }
+
+ /// Defines an option that takes multiple values.
+ ///
+ /// The [abbr] argument is a single-character string that can be used as a
+ /// shorthand for this option. For example, `abbr: "a"` will allow the user to
+ /// pass `-a value` or `-avalue`.
+ ///
+ /// The [help] argument is used by [usage] to describe this option.
+ ///
+ /// The [valueHelp] argument is used by [usage] as a name for the value this
+ /// argument takes. For example, `valueHelp: "FOO"` will include
+ /// `--option=<FOO>` rather than just `--option` in the usage string.
+ ///
+ /// The [allowed] argument is a list of valid values for this argument. If
+ /// it's non-`null` and the user passes a value that's not included in the
+ /// list, [parse] will throw a [FormatException]. The allowed values will also
+ /// be included in [usage].
+ ///
+ /// The [allowedHelp] argument is a map from values in [allowed] to
+ /// documentation for those values that will be included in [usage].
+ ///
+ /// The [defaultsTo] argument indicates the values this option will have if
+ /// the user doesn't explicitly pass it in (or `[]` by default).
+ ///
+ /// The [callback] argument is invoked with the option's value when the option
+ /// is parsed. Note that this makes argument parsing order-dependent in ways
+ /// that are often surprising, and its use is discouraged in favor of reading
+ /// values from the [ArgResults].
+ ///
+ /// If [splitCommas] is `true` (the default), multiple options may be passed
+ /// by writing `--option a,b` in addition to `--option a --option b`.
+ ///
+ /// If [hide] is `true`, this option won't be included in [usage].
+ ///
+ /// If [aliases] is provided, these are used as aliases for [name]. These
+ /// aliases will not appear as keys in the [options] map.
+ ///
+ /// Throws an [ArgumentError] if:
+ ///
+ /// * There is already an option with name [name].
+ /// * There is already an option using abbreviation [abbr].
+ void addMultiOption(String name,
+ {String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Iterable<String>? defaultsTo,
+ void Function(List<String>)? callback,
+ bool splitCommas = true,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ _addOption(
+ name,
+ abbr,
+ help,
+ valueHelp,
+ allowed,
+ allowedHelp,
+ defaultsTo?.toList() ?? <String>[],
+ callback == null ? null : (List<String> value) => callback(value),
+ OptionType.multiple,
+ splitCommas: splitCommas,
+ hide: hide,
+ aliases: aliases);
+ }
+
+ void _addOption(
+ String name,
+ String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Object? defaultsTo,
+ Function? callback,
+ OptionType type,
+ {bool negatable = false,
+ bool? splitCommas,
+ bool mandatory = false,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ var allNames = [name, ...aliases];
+ if (allNames.any((name) => findByNameOrAlias(name) != null)) {
+ throw ArgumentError('Duplicate option or alias "$name".');
+ }
+
+ // Make sure the abbreviation isn't too long or in use.
+ if (abbr != null) {
+ var existing = findByAbbreviation(abbr);
+ if (existing != null) {
+ throw ArgumentError(
+ 'Abbreviation "$abbr" is already used by "${existing.name}".');
+ }
+ }
+
+ // Make sure the option is not mandatory with a default value.
+ if (mandatory && defaultsTo != null) {
+ throw ArgumentError(
+ 'The option $name cannot be mandatory and have a default value.');
+ }
+
+ var option = newOption(name, abbr, help, valueHelp, allowed, allowedHelp,
+ defaultsTo, callback, type,
+ negatable: negatable,
+ splitCommas: splitCommas,
+ mandatory: mandatory,
+ hide: hide,
+ aliases: aliases);
+ _options[name] = option;
+ _optionsAndSeparators.add(option);
+ for (var alias in aliases) {
+ _aliases[alias] = name;
+ }
+ }
+
+ /// Adds a separator line to the usage.
+ ///
+ /// In the usage text for the parser, this will appear between any options
+ /// added before this call and ones added after it.
+ void addSeparator(String text) {
+ _optionsAndSeparators.add(text);
+ }
+
+ /// Parses [args], a list of command-line arguments, matches them against the
+ /// flags and options defined by this parser, and returns the result.
+ ArgResults parse(Iterable<String> args) =>
+ Parser(null, this, Queue.of(args)).parse();
+
+ /// Generates a string displaying usage information for the defined options.
+ ///
+ /// This is basically the help text shown on the command line.
+ String get usage {
+ return generateUsage(_optionsAndSeparators, lineLength: usageLineLength);
+ }
+
+ /// Returns the default value for [option].
+ dynamic defaultFor(String option) {
+ var value = findByNameOrAlias(option);
+ if (value == null) {
+ throw ArgumentError('No option named $option');
+ }
+ return value.defaultsTo;
+ }
+
+ @Deprecated('Use defaultFor instead.')
+ dynamic getDefault(String option) => defaultFor(option);
+
+ /// Finds the option whose abbreviation is [abbr], or `null` if no option has
+ /// that abbreviation.
+ Option? findByAbbreviation(String abbr) {
+ for (var option in options.values) {
+ if (option.abbr == abbr) return option;
+ }
+ return null;
+ }
+
+ /// Finds the option whose name or alias matches [name], or `null` if no
+ /// option has that name or alias.
+ Option? findByNameOrAlias(String name) => options[_aliases[name] ?? name];
+}
diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart
new file mode 100644
index 0000000..fbee82b
--- /dev/null
+++ b/pkgs/args/lib/src/arg_parser_exception.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// An exception thrown by `ArgParser`.
+class ArgParserException extends FormatException {
+ /// The command(s) that were parsed before discovering the error.
+ ///
+ /// This will be empty if the error was on the root parser.
+ final List<String> commands;
+
+ /// The name of the argument that was being parsed when the error was
+ /// discovered.
+ final String? argumentName;
+
+ ArgParserException(super.message,
+ [Iterable<String>? commands,
+ this.argumentName,
+ super.source,
+ super.offset])
+ : commands = commands == null ? const [] : List.unmodifiable(commands);
+}
diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart
new file mode 100644
index 0000000..72c4410
--- /dev/null
+++ b/pkgs/args/lib/src/arg_results.dart
@@ -0,0 +1,151 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'arg_parser.dart';
+
+/// Creates a new [ArgResults].
+///
+/// Since [ArgResults] doesn't have a public constructor, this lets [ArgParser]
+/// get to it. This function isn't exported to the public API of the package.
+ArgResults newArgResults(
+ ArgParser parser,
+ Map<String, dynamic> parsed,
+ String? name,
+ ArgResults? command,
+ List<String> rest,
+ List<String> arguments) {
+ return ArgResults._(parser, parsed, name, command, rest, arguments);
+}
+
+/// The results of parsing a series of command line arguments using
+/// [ArgParser.parse].
+///
+/// Includes the parsed options and any remaining unparsed command line
+/// arguments.
+class ArgResults {
+ /// The [ArgParser] whose options were parsed for these results.
+ final ArgParser _parser;
+
+ /// The option values that were parsed from arguments.
+ final Map<String, dynamic> _parsed;
+
+ /// The name of the command for which these options are parsed, or `null` if
+ /// these are the top-level results.
+ final String? name;
+
+ /// The command that was selected, or `null` if none was.
+ ///
+ /// This will contain the options that were selected for that command.
+ final ArgResults? command;
+
+ /// The remaining command-line arguments that were not parsed as options or
+ /// flags.
+ ///
+ /// If `--` was used to separate the options from the remaining arguments,
+ /// it will not be included in this list unless parsing stopped before the
+ /// `--` was reached.
+ final List<String> rest;
+
+ /// The original arguments that were parsed.
+ final List<String> arguments;
+
+ ArgResults._(this._parser, this._parsed, this.name, this.command,
+ List<String> rest, List<String> arguments)
+ : rest = UnmodifiableListView(rest),
+ arguments = UnmodifiableListView(arguments);
+
+ /// Returns the parsed or default command-line option named [name].
+ ///
+ /// [name] must be a valid option name in the parser.
+ ///
+ /// > [!Note]
+ /// > Callers should prefer using the more strongly typed methods - [flag] for
+ /// > flags, [option] for options, and [multiOption] for multi-options.
+ dynamic operator [](String name) {
+ if (!_parser.options.containsKey(name)) {
+ throw ArgumentError('Could not find an option named "--$name".');
+ }
+
+ final option = _parser.options[name]!;
+ if (option.mandatory && !_parsed.containsKey(name)) {
+ throw ArgumentError('Option $name is mandatory.');
+ }
+
+ return option.valueOrDefault(_parsed[name]);
+ }
+
+ /// Returns the parsed or default command-line flag named [name].
+ ///
+ /// [name] must be a valid flag name in the parser.
+ bool flag(String name) {
+ var option = _parser.options[name];
+ if (option == null) {
+ throw ArgumentError('Could not find an option named "--$name".');
+ }
+ if (!option.isFlag) {
+ throw ArgumentError('"$name" is not a flag.');
+ }
+ return option.valueOrDefault(_parsed[name]) as bool;
+ }
+
+ /// Returns the parsed or default command-line option named [name].
+ ///
+ /// [name] must be a valid option name in the parser.
+ String? option(String name) {
+ var option = _parser.options[name];
+ if (option == null) {
+ throw ArgumentError('Could not find an option named "--$name".');
+ }
+ if (!option.isSingle) {
+ throw ArgumentError('"$name" is a multi-option.');
+ }
+ return option.valueOrDefault(_parsed[name]) as String?;
+ }
+
+ /// Returns the list of parsed (or default) command-line options for [name].
+ ///
+ /// [name] must be a valid option name in the parser.
+ List<String> multiOption(String name) {
+ var option = _parser.options[name];
+ if (option == null) {
+ throw ArgumentError('Could not find an option named "--$name".');
+ }
+ if (!option.isMultiple) {
+ throw ArgumentError('"$name" is not a multi-option.');
+ }
+ return option.valueOrDefault(_parsed[name]) as List<String>;
+ }
+
+ /// The names of the available options.
+ ///
+ /// Includes the options whose values were parsed or that have defaults.
+ /// Options that weren't present and have no default are omitted.
+ Iterable<String> get options {
+ var result = _parsed.keys.toSet();
+
+ // Include the options that have defaults.
+ _parser.options.forEach((name, option) {
+ if (option.defaultsTo != null) result.add(name);
+ });
+
+ return result;
+ }
+
+ /// Returns `true` if the option with [name] was parsed from an actual
+ /// argument.
+ ///
+ /// Returns `false` if it wasn't provided and the default value or no default
+ /// value would be used instead.
+ ///
+ /// [name] must be a valid option name in the parser.
+ bool wasParsed(String name) {
+ if (!_parser.options.containsKey(name)) {
+ throw ArgumentError('Could not find an option named "--$name".');
+ }
+
+ return _parsed.containsKey(name);
+ }
+}
diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart
new file mode 100644
index 0000000..f04d014
--- /dev/null
+++ b/pkgs/args/lib/src/help_command.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../command_runner.dart';
+
+/// The built-in help command that's added to every [CommandRunner].
+///
+/// This command displays help information for the various subcommands.
+class HelpCommand<T> extends Command<T> {
+ @override
+ final name = 'help';
+
+ @override
+ String get description =>
+ 'Display help information for ${runner!.executableName}.';
+
+ @override
+ String get invocation => '${runner!.executableName} help [command]';
+
+ @override
+ bool get hidden => true;
+
+ @override
+ Null run() {
+ // Show the default help if no command was specified.
+ if (argResults!.rest.isEmpty) {
+ runner!.printUsage();
+ return;
+ }
+
+ // Walk the command tree to show help for the selected command or
+ // subcommand.
+ var commands = runner!.commands;
+ Command<T>? command;
+ var commandString = runner!.executableName;
+
+ for (var name in argResults!.rest) {
+ if (commands.isEmpty) {
+ command!.usageException(
+ 'Command "$commandString" does not expect a subcommand.');
+ }
+
+ if (commands[name] == null) {
+ if (command == null) {
+ runner!.usageException('Could not find a command named "$name".');
+ }
+
+ command.usageException(
+ 'Could not find a subcommand named "$name" for "$commandString".');
+ }
+
+ command = commands[name];
+ commands = command!.subcommands;
+ commandString += ' $name';
+ }
+
+ command!.printUsage();
+ }
+}
diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart
new file mode 100644
index 0000000..50a8628
--- /dev/null
+++ b/pkgs/args/lib/src/option.dart
@@ -0,0 +1,192 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Creates a new [Option].
+///
+/// Since [Option] doesn't have a public constructor, this lets `ArgParser`
+/// get to it. This function isn't exported to the public API of the package.
+Option newOption(
+ String name,
+ String? abbr,
+ String? help,
+ String? valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ Object? defaultsTo,
+ Function? callback,
+ OptionType type,
+ {bool? negatable,
+ bool? splitCommas,
+ bool mandatory = false,
+ bool hide = false,
+ List<String> aliases = const []}) {
+ return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo,
+ callback, type,
+ negatable: negatable,
+ splitCommas: splitCommas,
+ mandatory: mandatory,
+ hide: hide,
+ aliases: aliases);
+}
+
+/// A command-line option.
+///
+/// This represents both boolean flags and options which take a value.
+class Option {
+ /// The name of the option that the user passes as an argument.
+ final String name;
+
+ /// A single-character string that can be used as a shorthand for this option.
+ ///
+ /// For example, `abbr: "a"` will allow the user to pass `-a value` or
+ /// `-avalue`.
+ final String? abbr;
+
+ /// A description of this option.
+ final String? help;
+
+ /// A name for the value this option takes.
+ final String? valueHelp;
+
+ /// A list of valid values for this option.
+ final List<String>? allowed;
+
+ /// A map from values in [allowed] to documentation for those values.
+ final Map<String, String>? allowedHelp;
+
+ /// The value this option will have if the user doesn't explicitly pass it.
+ final dynamic defaultsTo;
+
+ /// Whether this flag's value can be set to `false`.
+ ///
+ /// For example, if [name] is `flag`, the user can pass `--no-flag` to set its
+ /// value to `false`.
+ ///
+ /// This is `null` unless [type] is [OptionType.flag].
+ final bool? negatable;
+
+ /// The callback to invoke with the option's value when the option is parsed.
+ final Function? callback;
+
+ /// Whether this is a flag, a single value option, or a multi-value option.
+ final OptionType type;
+
+ /// Whether multiple values may be passed by writing `--option a,b` in
+ /// addition to `--option a --option b`.
+ final bool splitCommas;
+
+ /// Whether this option must be provided for correct usage.
+ final bool mandatory;
+
+ /// Whether this option should be hidden from usage documentation.
+ final bool hide;
+
+ /// All aliases for [name].
+ final List<String> aliases;
+
+ /// Whether the option is boolean-valued flag.
+ bool get isFlag => type == OptionType.flag;
+
+ /// Whether the option takes a single value.
+ bool get isSingle => type == OptionType.single;
+
+ /// Whether the option allows multiple values.
+ bool get isMultiple => type == OptionType.multiple;
+
+ Option._(
+ this.name,
+ this.abbr,
+ this.help,
+ this.valueHelp,
+ Iterable<String>? allowed,
+ Map<String, String>? allowedHelp,
+ this.defaultsTo,
+ this.callback,
+ this.type,
+ {this.negatable,
+ bool? splitCommas,
+ this.mandatory = false,
+ this.hide = false,
+ this.aliases = const []})
+ : allowed = allowed == null ? null : List.unmodifiable(allowed),
+ allowedHelp =
+ allowedHelp == null ? null : Map.unmodifiable(allowedHelp),
+ // If the user doesn't specify [splitCommas], it defaults to true for
+ // multiple options.
+ splitCommas = splitCommas ?? type == OptionType.multiple {
+ if (name.isEmpty) {
+ throw ArgumentError('Name cannot be empty.');
+ } else if (name.startsWith('-')) {
+ throw ArgumentError('Name $name cannot start with "-".');
+ }
+
+ // Ensure name does not contain any invalid characters.
+ if (_invalidChars.hasMatch(name)) {
+ throw ArgumentError('Name "$name" contains invalid characters.');
+ }
+
+ var abbr = this.abbr;
+ if (abbr != null) {
+ if (abbr.length != 1) {
+ throw ArgumentError('Abbreviation must be null or have length 1.');
+ } else if (abbr == '-') {
+ throw ArgumentError('Abbreviation cannot be "-".');
+ }
+
+ if (_invalidChars.hasMatch(abbr)) {
+ throw ArgumentError('Abbreviation is an invalid character.');
+ }
+ }
+ }
+
+ /// Returns [value] if non-`null`, otherwise returns the default value for
+ /// this option.
+ ///
+ /// For single-valued options, it will be [defaultsTo] if set or `null`
+ /// otherwise. For multiple-valued options, it will be an empty list or a
+ /// list containing [defaultsTo] if set.
+ dynamic valueOrDefault(Object? value) {
+ if (value != null) return value;
+ if (isMultiple) return defaultsTo ?? <String>[];
+ return defaultsTo;
+ }
+
+ @Deprecated('Use valueOrDefault instead.')
+ dynamic getOrDefault(Object? value) => valueOrDefault(value);
+
+ static final _invalidChars = RegExp(r'''[ \t\r\n"'\\/]''');
+}
+
+/// What kinds of values an option accepts.
+class OptionType {
+ /// An option that can only be `true` or `false`.
+ ///
+ /// The presence of the option name itself in the argument list means `true`.
+ static const flag = OptionType._('OptionType.flag');
+
+ /// An option that takes a single value.
+ ///
+ /// Examples:
+ ///
+ /// --mode debug
+ /// -mdebug
+ /// --mode=debug
+ ///
+ /// If the option is passed more than once, the last one wins.
+ static const single = OptionType._('OptionType.single');
+
+ /// An option that allows multiple values.
+ ///
+ /// Example:
+ ///
+ /// --output text --output xml
+ ///
+ /// In the parsed `ArgResults`, a multiple-valued option will always return
+ /// a list, even if one or no values were passed.
+ static const multiple = OptionType._('OptionType.multiple');
+
+ final String name;
+
+ const OptionType._(this.name);
+}
diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart
new file mode 100644
index 0000000..660e56d
--- /dev/null
+++ b/pkgs/args/lib/src/parser.dart
@@ -0,0 +1,381 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:collection';
+
+import 'arg_parser.dart';
+import 'arg_parser_exception.dart';
+import 'arg_results.dart';
+import 'option.dart';
+
+/// The actual argument parsing class.
+///
+/// Unlike [ArgParser] which is really more an "arg grammar", this is the class
+/// that does the parsing and holds the mutable state required during a parse.
+class Parser {
+ /// If parser is parsing a command's options, this will be the name of the
+ /// command. For top-level results, this returns `null`.
+ final String? _commandName;
+
+ /// The parser for the supercommand of this command parser, or `null` if this
+ /// is the top-level parser.
+ final Parser? _parent;
+
+ /// The grammar being parsed.
+ final ArgParser _grammar;
+
+ /// The arguments being parsed.
+ final Queue<String> _args;
+
+ /// The remaining non-option, non-command arguments.
+ final List<String> _rest;
+
+ /// The accumulated parsed options.
+ final Map<String, dynamic> _results = <String, dynamic>{};
+
+ Parser(this._commandName, this._grammar, this._args,
+ [this._parent, List<String>? rest])
+ : _rest = [...?rest];
+
+ /// The current argument being parsed.
+ String get _current => _args.first;
+
+ /// Parses the arguments. This can only be called once.
+ ArgResults parse() {
+ var arguments = _args.toList();
+ if (_grammar.allowsAnything) {
+ return newArgResults(
+ _grammar, const {}, _commandName, null, arguments, arguments);
+ }
+
+ ArgResults? commandResults;
+
+ // Parse the args.
+ while (_args.isNotEmpty) {
+ if (_current == '--') {
+ // Reached the argument terminator, so stop here.
+ _args.removeFirst();
+ break;
+ }
+
+ // Try to parse the current argument as a command. This happens before
+ // options so that commands can have option-like names.
+ var command = _grammar.commands[_current];
+ if (command != null) {
+ _validate(_rest.isEmpty, 'Cannot specify arguments before a command.',
+ _current);
+ var commandName = _args.removeFirst();
+ var commandParser = Parser(commandName, command, _args, this, _rest);
+
+ try {
+ commandResults = commandParser.parse();
+ } on ArgParserException catch (error) {
+ throw ArgParserException(
+ error.message,
+ [commandName, ...error.commands],
+ error.argumentName,
+ error.source,
+ error.offset);
+ }
+
+ // All remaining arguments were passed to command so clear them here.
+ _rest.clear();
+ break;
+ }
+
+ // Try to parse the current argument as an option. Note that the order
+ // here matters.
+ if (_parseSoloOption()) continue;
+ if (_parseAbbreviation(this)) continue;
+ if (_parseLongOption()) continue;
+
+ // This argument is neither option nor command, so stop parsing unless
+ // the [allowTrailingOptions] option is set.
+ if (!_grammar.allowTrailingOptions) break;
+ _rest.add(_args.removeFirst());
+ }
+
+ // Check if mandatory and invoke existing callbacks.
+ _grammar.options.forEach((name, option) {
+ var parsedOption = _results[name];
+
+ var callback = option.callback;
+ if (callback == null) return;
+
+ // Check if an option is mandatory and was passed; if not, throw an
+ // exception.
+ if (option.mandatory && parsedOption == null) {
+ throw ArgParserException('Option $name is mandatory.', null, name);
+ }
+
+ // ignore: avoid_dynamic_calls
+ callback(option.valueOrDefault(parsedOption));
+ });
+
+ // Add in the leftover arguments we didn't parse to the innermost command.
+ _rest.addAll(_args);
+ _args.clear();
+ return newArgResults(
+ _grammar, _results, _commandName, commandResults, _rest, arguments);
+ }
+
+ /// Pulls the value for [option] from the second argument in [_args].
+ ///
+ /// Validates that there is a valid value there.
+ void _readNextArgAsValue(Option option, String arg) {
+ // Take the option argument from the next command line arg.
+ _validate(_args.isNotEmpty, 'Missing argument for "$arg".', arg);
+
+ _setOption(_results, option, _current, arg);
+ _args.removeFirst();
+ }
+
+ /// Tries to parse the current argument as a "solo" option, which is a single
+ /// hyphen followed by a single letter.
+ ///
+ /// We treat this differently than collapsed abbreviations (like "-abc") to
+ /// handle the possible value that may follow it.
+ bool _parseSoloOption() {
+ // Hand coded regexp: r'^-([a-zA-Z0-9])$'
+ // Length must be two, hyphen followed by any letter/digit.
+ if (_current.length != 2) return false;
+ if (!_current.startsWith('-')) return false;
+ var opt = _current[1];
+ if (!_isLetterOrDigit(opt.codeUnitAt(0))) return false;
+ return _handleSoloOption(opt);
+ }
+
+ bool _handleSoloOption(String opt) {
+ var option = _grammar.findByAbbreviation(opt);
+ if (option == null) {
+ // Walk up to the parent command if possible.
+ _validate(_parent != null, 'Could not find an option or flag "-$opt".',
+ '-$opt');
+ return _parent!._handleSoloOption(opt);
+ }
+
+ _args.removeFirst();
+
+ if (option.isFlag) {
+ _setFlag(_results, option, true);
+ } else {
+ _readNextArgAsValue(option, '-$opt');
+ }
+
+ return true;
+ }
+
+ /// Tries to parse the current argument as a series of collapsed abbreviations
+ /// (like "-abc") or a single abbreviation with the value directly attached
+ /// to it (like "-mrelease").
+ bool _parseAbbreviation(Parser innermostCommand) {
+ // Hand coded regexp: r'^-([a-zA-Z0-9]+)(.*)$'
+ // Hyphen then at least one letter/digit then zero or more
+ // anything-but-newlines.
+ if (_current.length < 2) return false;
+ if (!_current.startsWith('-')) return false;
+
+ // Find where we go from letters/digits to rest.
+ var index = 1;
+ while (index < _current.length &&
+ _isLetterOrDigit(_current.codeUnitAt(index))) {
+ ++index;
+ }
+ // Must be at least one letter/digit.
+ if (index == 1) return false;
+
+ // If the first character is the abbreviation for a non-flag option, then
+ // the rest is the value.
+ var lettersAndDigits = _current.substring(1, index);
+ var rest = _current.substring(index);
+ if (rest.contains('\n') || rest.contains('\r')) return false;
+ return _handleAbbreviation(lettersAndDigits, rest, innermostCommand);
+ }
+
+ bool _handleAbbreviation(
+ String lettersAndDigits, String rest, Parser innermostCommand) {
+ var c = lettersAndDigits.substring(0, 1);
+ var first = _grammar.findByAbbreviation(c);
+ if (first == null) {
+ // Walk up to the parent command if possible.
+ _validate(_parent != null,
+ 'Could not find an option with short name "-$c".', '-$c');
+ return _parent!
+ ._handleAbbreviation(lettersAndDigits, rest, innermostCommand);
+ } else if (!first.isFlag) {
+ // The first character is a non-flag option, so the rest must be the
+ // value.
+ var value = '${lettersAndDigits.substring(1)}$rest';
+ _setOption(_results, first, value, '-$c');
+ } else {
+ // If we got some non-flag characters, then it must be a value, but
+ // if we got here, it's a flag, which is wrong.
+ _validate(
+ rest == '',
+ 'Option "-$c" is a flag and cannot handle value '
+ '"${lettersAndDigits.substring(1)}$rest".',
+ '-$c');
+
+ // Not an option, so all characters should be flags.
+ // We use "innermostCommand" here so that if a parent command parses the
+ // *first* letter, subcommands can still be found to parse the other
+ // letters.
+ for (var i = 0; i < lettersAndDigits.length; i++) {
+ var c = lettersAndDigits.substring(i, i + 1);
+ innermostCommand._parseShortFlag(c);
+ }
+ }
+
+ _args.removeFirst();
+ return true;
+ }
+
+ void _parseShortFlag(String c) {
+ var option = _grammar.findByAbbreviation(c);
+ if (option == null) {
+ // Walk up to the parent command if possible.
+ _validate(_parent != null,
+ 'Could not find an option with short name "-$c".', '-$c');
+ _parent!._parseShortFlag(c);
+ return;
+ }
+
+ // In a list of short options, only the first can be a non-flag. If
+ // we get here we've checked that already.
+ _validate(option.isFlag,
+ 'Option "-$c" must be a flag to be in a collapsed "-".', '-$c');
+
+ _setFlag(_results, option, true);
+ }
+
+ /// Tries to parse the current argument as a long-form named option, which
+ /// may include a value like "--mode=release" or "--mode release".
+ bool _parseLongOption() {
+ // Hand coded regexp: r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'
+ // Two hyphens then at least one letter/digit/hyphen, optionally an equal
+ // sign followed by zero or more anything-but-newlines.
+
+ if (!_current.startsWith('--')) return false;
+
+ var index = _current.indexOf('=');
+ var name =
+ index == -1 ? _current.substring(2) : _current.substring(2, index);
+ for (var i = 0; i != name.length; ++i) {
+ if (!_isLetterDigitHyphenOrUnderscore(name.codeUnitAt(i))) return false;
+ }
+ var value = index == -1 ? null : _current.substring(index + 1);
+ if (value != null && (value.contains('\n') || value.contains('\r'))) {
+ return false;
+ }
+ return _handleLongOption(name, value);
+ }
+
+ bool _handleLongOption(String name, String? value) {
+ var option = _grammar.findByNameOrAlias(name);
+ if (option != null) {
+ _args.removeFirst();
+ if (option.isFlag) {
+ _validate(value == null,
+ 'Flag option "--$name" should not be given a value.', '--$name');
+
+ _setFlag(_results, option, true);
+ } else if (value != null) {
+ // We have a value like --foo=bar.
+ _setOption(_results, option, value, '--$name');
+ } else {
+ // Option like --foo, so look for the value as the next arg.
+ _readNextArgAsValue(option, '--$name');
+ }
+ } else if (name.startsWith('no-')) {
+ // See if it's a negated flag.
+ var positiveName = name.substring('no-'.length);
+ option = _grammar.findByNameOrAlias(positiveName);
+ if (option == null) {
+ // Walk up to the parent command if possible.
+ _validate(_parent != null, 'Could not find an option named "--$name".',
+ '--$name');
+ return _parent!._handleLongOption(name, value);
+ }
+
+ _args.removeFirst();
+ _validate(
+ option.isFlag, 'Cannot negate non-flag option "--$name".', '--$name');
+ _validate(
+ option.negatable!, 'Cannot negate option "--$name".', '--$name');
+
+ _setFlag(_results, option, false);
+ } else {
+ // Walk up to the parent command if possible.
+ _validate(_parent != null, 'Could not find an option named "--$name".',
+ '--$name');
+ return _parent!._handleLongOption(name, value);
+ }
+
+ return true;
+ }
+
+ /// Called during parsing to validate the arguments.
+ ///
+ /// Throws an [ArgParserException] if [condition] is `false`.
+ void _validate(bool condition, String message,
+ [String? args, List<String>? source, int? offset]) {
+ if (!condition) {
+ throw ArgParserException(message, null, args, source, offset);
+ }
+ }
+
+ /// Validates and stores [value] as the value for [option], which must not be
+ /// a flag.
+ void _setOption(Map results, Option option, String value, String arg) {
+ assert(!option.isFlag);
+
+ if (!option.isMultiple) {
+ _validateAllowed(option, value, arg);
+ results[option.name] = value;
+ return;
+ }
+
+ var list = results.putIfAbsent(option.name, () => <String>[]) as List;
+
+ if (option.splitCommas) {
+ for (var element in value.split(',')) {
+ _validateAllowed(option, element, arg);
+ list.add(element);
+ }
+ } else {
+ _validateAllowed(option, value, arg);
+ list.add(value);
+ }
+ }
+
+ /// Validates and stores [value] as the value for [option], which must be a
+ /// flag.
+ void _setFlag(Map results, Option option, bool value) {
+ assert(option.isFlag);
+ results[option.name] = value;
+ }
+
+ /// Validates that [value] is allowed as a value of [option].
+ void _validateAllowed(Option option, String value, String arg) {
+ if (option.allowed == null) return;
+
+ _validate(option.allowed!.contains(value),
+ '"$value" is not an allowed value for option "$arg".', arg);
+ }
+}
+
+bool _isLetterOrDigit(int codeUnit) =>
+ // Uppercase letters.
+ (codeUnit >= 65 && codeUnit <= 90) ||
+ // Lowercase letters.
+ (codeUnit >= 97 && codeUnit <= 122) ||
+ // Digits.
+ (codeUnit >= 48 && codeUnit <= 57);
+
+bool _isLetterDigitHyphenOrUnderscore(int codeUnit) =>
+ _isLetterOrDigit(codeUnit) ||
+ // Hyphen.
+ codeUnit == 45 ||
+ // Underscore.
+ codeUnit == 95;
diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart
new file mode 100644
index 0000000..1ef9627
--- /dev/null
+++ b/pkgs/args/lib/src/usage.dart
@@ -0,0 +1,255 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:math' as math;
+
+import '../args.dart';
+import 'utils.dart';
+
+/// Generates a string of usage (i.e. help) text for a list of options.
+///
+/// Internally, it works like a tabular printer. The output is divided into
+/// three horizontal columns, like so:
+///
+/// -h, --help Prints the usage information
+/// | | | |
+///
+/// It builds the usage text up one column at a time and handles padding with
+/// spaces and wrapping to the next line to keep the cells correctly lined up.
+///
+/// [lineLength] specifies the horizontal character position at which the help
+/// text is wrapped. Help that extends past this column will be wrapped at the
+/// nearest whitespace (or truncated if there is no available whitespace). If
+/// `null` there will not be any wrapping.
+String generateUsage(List optionsAndSeparators, {int? lineLength}) =>
+ _Usage(optionsAndSeparators, lineLength).generate();
+
+class _Usage {
+ /// Abbreviation, long name, help.
+ static const _columnCount = 3;
+
+ /// A list of the [Option]s intermingled with [String] separators.
+ final List _optionsAndSeparators;
+
+ /// The working buffer for the generated usage text.
+ final _buffer = StringBuffer();
+
+ /// The column that the "cursor" is currently on.
+ ///
+ /// If the next call to [write()] is not for this column, it will correctly
+ /// handle advancing to the next column (and possibly the next row).
+ int _currentColumn = 0;
+
+ /// The width in characters of each column.
+ late final _columnWidths = _calculateColumnWidths();
+
+ /// How many newlines need to be rendered before the next bit of text can be
+ /// written.
+ ///
+ /// We do this lazily so that the last bit of usage doesn't have dangling
+ /// newlines. We only write newlines right *before* we write some real
+ /// content.
+ int _newlinesNeeded = 0;
+
+ /// The horizontal character position at which help text is wrapped.
+ ///
+ /// Help that extends past this column will be wrapped at the nearest
+ /// whitespace (or truncated if there is no available whitespace).
+ final int? lineLength;
+
+ _Usage(this._optionsAndSeparators, this.lineLength);
+
+ /// Generates a string displaying usage information for the defined options.
+ /// This is basically the help text shown on the command line.
+ String generate() {
+ for (var optionOrSeparator in _optionsAndSeparators) {
+ if (optionOrSeparator is String) {
+ _writeSeparator(optionOrSeparator);
+ continue;
+ }
+ var option = optionOrSeparator as Option;
+ if (option.hide) continue;
+ _writeOption(option);
+ }
+
+ return _buffer.toString();
+ }
+
+ void _writeSeparator(String separator) {
+ // Ensure that there's always a blank line before a separator.
+ if (_buffer.isNotEmpty) _buffer.write('\n\n');
+ _buffer.write(separator);
+ _newlinesNeeded = 1;
+ }
+
+ void _writeOption(Option option) {
+ _write(0, _abbreviation(option));
+ _write(1, '${_longOption(option)}${_mandatoryOption(option)}');
+
+ if (option.help != null) _write(2, option.help!);
+
+ if (option.allowedHelp != null) {
+ var allowedNames = option.allowedHelp!.keys.toList();
+ allowedNames.sort();
+ _newline();
+ for (var name in allowedNames) {
+ _write(1, _allowedTitle(option, name));
+ _write(2, option.allowedHelp![name]!);
+ }
+ _newline();
+ } else if (option.allowed != null) {
+ _write(2, _buildAllowedList(option));
+ } else if (option.isFlag) {
+ if (option.defaultsTo == true) {
+ _write(2, '(defaults to on)');
+ }
+ } else if (option.isMultiple) {
+ if (option.defaultsTo != null &&
+ (option.defaultsTo as Iterable).isNotEmpty) {
+ var defaults =
+ (option.defaultsTo as List).map((value) => '"$value"').join(', ');
+ _write(2, '(defaults to $defaults)');
+ }
+ } else if (option.defaultsTo != null) {
+ _write(2, '(defaults to "${option.defaultsTo}")');
+ }
+ }
+
+ String _abbreviation(Option option) =>
+ option.abbr == null ? '' : '-${option.abbr}, ';
+
+ String _longOption(Option option) {
+ String result;
+ if (option.negatable!) {
+ result = '--[no-]${option.name}';
+ } else {
+ result = '--${option.name}';
+ }
+
+ if (option.valueHelp != null) result += '=<${option.valueHelp}>';
+
+ return result;
+ }
+
+ String _mandatoryOption(Option option) {
+ return option.mandatory ? ' (mandatory)' : '';
+ }
+
+ String _allowedTitle(Option option, String allowed) {
+ var isDefault = option.defaultsTo is List
+ ? (option.defaultsTo as List).contains(allowed)
+ : option.defaultsTo == allowed;
+ return ' [$allowed]${isDefault ? ' (default)' : ''}';
+ }
+
+ List<int> _calculateColumnWidths() {
+ var abbr = 0;
+ var title = 0;
+ for (var option in _optionsAndSeparators) {
+ if (option is! Option) continue;
+ if (option.hide) continue;
+
+ // Make room in the first column if there are abbreviations.
+ abbr = math.max(abbr, _abbreviation(option).length);
+
+ // Make room for the option.
+ title = math.max(
+ title, _longOption(option).length + _mandatoryOption(option).length);
+
+ // Make room for the allowed help.
+ if (option.allowedHelp != null) {
+ for (var allowed in option.allowedHelp!.keys) {
+ title = math.max(title, _allowedTitle(option, allowed).length);
+ }
+ }
+ }
+
+ // Leave a gutter between the columns.
+ title += 4;
+ return [abbr, title];
+ }
+
+ void _newline() {
+ _newlinesNeeded++;
+ _currentColumn = 0;
+ }
+
+ void _write(int column, String text) {
+ var lines = text.split('\n');
+ // If we are writing the last column, word wrap it to fit.
+ if (column == _columnWidths.length && lineLength != null) {
+ var start =
+ _columnWidths.take(column).reduce((start, width) => start + width);
+ lines = [
+ for (var line in lines)
+ ...wrapTextAsLines(line, start: start, length: lineLength),
+ ];
+ }
+
+ // Strip leading and trailing empty lines.
+ while (lines.isNotEmpty && lines.first.trim() == '') {
+ lines.removeAt(0);
+ }
+ while (lines.isNotEmpty && lines.last.trim() == '') {
+ lines.removeLast();
+ }
+
+ for (var line in lines) {
+ _writeLine(column, line);
+ }
+ }
+
+ void _writeLine(int column, String text) {
+ // Write any pending newlines.
+ while (_newlinesNeeded > 0) {
+ _buffer.write('\n');
+ _newlinesNeeded--;
+ }
+
+ // Advance until we are at the right column (which may mean wrapping around
+ // to the next line.
+ while (_currentColumn != column) {
+ if (_currentColumn < _columnCount - 1) {
+ _buffer.write(' ' * _columnWidths[_currentColumn]);
+ } else {
+ _buffer.write('\n');
+ }
+ _currentColumn = (_currentColumn + 1) % _columnCount;
+ }
+
+ if (column < _columnWidths.length) {
+ // Fixed-size column, so pad it.
+ _buffer.write(text.padRight(_columnWidths[column]));
+ } else {
+ // The last column, so just write it.
+ _buffer.write(text);
+ }
+
+ // Advance to the next column.
+ _currentColumn = (_currentColumn + 1) % _columnCount;
+
+ // If we reached the last column, we need to wrap to the next line.
+ if (column == _columnCount - 1) _newlinesNeeded++;
+ }
+
+ String _buildAllowedList(Option option) {
+ var isDefault = option.defaultsTo is List
+ ? (option.defaultsTo as List).contains
+ : (String value) => value == option.defaultsTo;
+
+ var allowedBuffer = StringBuffer();
+ allowedBuffer.write('[');
+ var first = true;
+ for (var allowed in option.allowed!) {
+ if (!first) allowedBuffer.write(', ');
+ allowedBuffer.write(allowed);
+ if (isDefault(allowed)) {
+ allowedBuffer.write(' (default)');
+ }
+ first = false;
+ }
+ allowedBuffer.write(']');
+ return allowedBuffer.toString();
+ }
+}
diff --git a/pkgs/args/lib/src/usage_exception.dart b/pkgs/args/lib/src/usage_exception.dart
new file mode 100644
index 0000000..fc8910e
--- /dev/null
+++ b/pkgs/args/lib/src/usage_exception.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+class UsageException implements Exception {
+ final String message;
+ final String usage;
+
+ UsageException(this.message, this.usage);
+
+ @override
+ String toString() => '$message\n\n$usage';
+}
diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart
new file mode 100644
index 0000000..ae5e093
--- /dev/null
+++ b/pkgs/args/lib/src/utils.dart
@@ -0,0 +1,143 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'dart:math' as math;
+
+/// Pads [source] to [length] by adding spaces at the end.
+String padRight(String source, int length) =>
+ source + ' ' * (length - source.length);
+
+/// Wraps a block of text into lines no longer than [length].
+///
+/// Tries to split at whitespace, but if that's not good enough to keep it
+/// under the limit, then it splits in the middle of a word.
+///
+/// Preserves indentation (leading whitespace) for each line (delimited by '\n')
+/// in the input, and indents wrapped lines the same amount.
+///
+/// If [hangingIndent] is supplied, then that many spaces are added to each
+/// line, except for the first line. This is useful for flowing text with a
+/// heading prefix (e.g. "Usage: "):
+///
+/// ```dart
+/// var prefix = "Usage: ";
+/// print(
+/// prefix + wrapText(invocation, hangingIndent: prefix.length, length: 40),
+/// );
+/// ```
+///
+/// yields:
+/// ```
+/// Usage: app main_command <subcommand>
+/// [arguments]
+/// ```
+///
+/// If [length] is not specified, then no wrapping occurs, and the original
+/// [text] is returned unchanged.
+String wrapText(String text, {int? length, int? hangingIndent}) {
+ if (length == null) return text;
+ hangingIndent ??= 0;
+ var splitText = text.split('\n');
+ var result = <String>[];
+ for (var line in splitText) {
+ var trimmedText = line.trimLeft();
+ final leadingWhitespace =
+ line.substring(0, line.length - trimmedText.length);
+ List<String> notIndented;
+ if (hangingIndent != 0) {
+ // When we have a hanging indent, we want to wrap the first line at one
+ // width, and the rest at another (offset by hangingIndent), so we wrap
+ // them twice and recombine.
+ var firstLineWrap = wrapTextAsLines(trimmedText,
+ length: length - leadingWhitespace.length);
+ notIndented = [firstLineWrap.removeAt(0)];
+ trimmedText = trimmedText.substring(notIndented[0].length).trimLeft();
+ if (firstLineWrap.isNotEmpty) {
+ notIndented.addAll(wrapTextAsLines(trimmedText,
+ length: length - leadingWhitespace.length - hangingIndent));
+ }
+ } else {
+ notIndented = wrapTextAsLines(trimmedText,
+ length: length - leadingWhitespace.length);
+ }
+ String? hangingIndentString;
+ result.addAll(notIndented.map<String>((String line) {
+ // Don't return any lines with just whitespace on them.
+ if (line.isEmpty) return '';
+ var result = '${hangingIndentString ?? ''}$leadingWhitespace$line';
+ hangingIndentString ??= ' ' * hangingIndent!;
+ return result;
+ }));
+ }
+ return result.join('\n');
+}
+
+/// Wraps a block of text into lines no longer than [length],
+/// starting at the [start] column, and returns the result as a list of strings.
+///
+/// Tries to split at whitespace, but if that's not good enough to keep it
+/// under the limit, then splits in the middle of a word. Preserves embedded
+/// newlines, but not indentation (it trims whitespace from each line).
+///
+/// If [length] is not specified, then no wrapping occurs, and the original
+/// [text] is returned after splitting it on newlines. Whitespace is not trimmed
+/// in this case.
+List<String> wrapTextAsLines(String text, {int start = 0, int? length}) {
+ assert(start >= 0);
+
+ /// Returns true if the code unit at [index] in [text] is a whitespace
+ /// character.
+ ///
+ /// Based on: https://en.wikipedia.org/wiki/Whitespace_character#Unicode
+ bool isWhitespace(String text, int index) {
+ var rune = text.codeUnitAt(index);
+ return rune >= 0x0009 && rune <= 0x000D ||
+ rune == 0x0020 ||
+ rune == 0x0085 ||
+ rune == 0x1680 ||
+ rune == 0x180E ||
+ rune >= 0x2000 && rune <= 0x200A ||
+ rune == 0x2028 ||
+ rune == 0x2029 ||
+ rune == 0x202F ||
+ rune == 0x205F ||
+ rune == 0x3000 ||
+ rune == 0xFEFF;
+ }
+
+ if (length == null) return text.split('\n');
+
+ var result = <String>[];
+ var effectiveLength = math.max(length - start, 10);
+ for (var line in text.split('\n')) {
+ line = line.trim();
+ if (line.length <= effectiveLength) {
+ result.add(line);
+ continue;
+ }
+
+ var currentLineStart = 0;
+ int? lastWhitespace;
+ for (var i = 0; i < line.length; ++i) {
+ if (isWhitespace(line, i)) lastWhitespace = i;
+
+ if (i - currentLineStart >= effectiveLength) {
+ // Back up to the last whitespace, unless there wasn't any, in which
+ // case we just split where we are.
+ if (lastWhitespace != null) i = lastWhitespace;
+
+ result.add(line.substring(currentLineStart, i).trim());
+
+ // Skip any intervening whitespace.
+ while (isWhitespace(line, i) && i < line.length) {
+ i++;
+ }
+
+ currentLineStart = i;
+ lastWhitespace = null;
+ }
+ }
+ result.add(line.substring(currentLineStart).trim());
+ }
+ return result;
+}
diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml
new file mode 100644
index 0000000..859e186
--- /dev/null
+++ b/pkgs/args/pubspec.yaml
@@ -0,0 +1,16 @@
+name: args
+version: 2.6.0
+description: >-
+ Library for defining parsers for parsing raw command-line arguments into a set
+ of options and values using GNU and POSIX style options.
+repository: https://github.com/dart-lang/core/main/pkgs/args
+
+topics:
+ - cli
+
+environment:
+ sdk: ^3.3.0
+
+dev_dependencies:
+ dart_flutter_team_lints: ^3.0.0
+ test: ^1.16.0
diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart
new file mode 100644
index 0000000..52e22b7
--- /dev/null
+++ b/pkgs/args/test/allow_anything_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:args/command_runner.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ group('new ArgParser.allowAnything()', () {
+ late ArgParser parser;
+ setUp(() {
+ parser = ArgParser.allowAnything();
+ });
+
+ test('exposes empty values', () {
+ expect(parser.options, isEmpty);
+ expect(parser.commands, isEmpty);
+ expect(parser.allowTrailingOptions, isFalse);
+ expect(parser.allowsAnything, isTrue);
+ expect(parser.usage, isEmpty);
+ expect(parser.findByAbbreviation('a'), isNull);
+ });
+
+ test('mutation methods throw errors', () {
+ expect(() => parser.addCommand('command'), throwsUnsupportedError);
+ expect(() => parser.addFlag('flag'), throwsUnsupportedError);
+ expect(() => parser.addOption('option'), throwsUnsupportedError);
+ expect(() => parser.addSeparator('==='), throwsUnsupportedError);
+ });
+
+ test('getDefault() throws an error', () {
+ expect(() => parser.defaultFor('option'), throwsArgumentError);
+ });
+
+ test('parses all values as rest arguments', () {
+ var results = parser.parse(['--foo', '-abc', '--', 'bar']);
+ expect(results.options, isEmpty);
+ expect(results.rest, equals(['--foo', '-abc', '--', 'bar']));
+ expect(results.arguments, equals(['--foo', '-abc', '--', 'bar']));
+ expect(results.command, isNull);
+ expect(results.name, isNull);
+ });
+
+ test('works as a subcommand', () {
+ var commandParser = ArgParser()..addCommand('command', parser);
+ var results =
+ commandParser.parse(['command', '--foo', '-abc', '--', 'bar']);
+ expect(results.command!.options, isEmpty);
+ expect(results.command!.rest, equals(['--foo', '-abc', '--', 'bar']));
+ expect(
+ results.command!.arguments, equals(['--foo', '-abc', '--', 'bar']));
+ expect(results.command!.name, equals('command'));
+ });
+
+ test('works as a subcommand in a CommandRunner', () async {
+ var commandRunner =
+ CommandRunner<void>('command', 'Description of command');
+ var command = AllowAnythingCommand();
+ commandRunner.addCommand(command);
+
+ await commandRunner.run([command.name, '--foo', '--bar', '-b', 'qux']);
+ });
+ });
+}
diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart
new file mode 100644
index 0000000..04bbc47
--- /dev/null
+++ b/pkgs/args/test/args_test.dart
@@ -0,0 +1,349 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ group('ArgParser.addFlag()', () {
+ test('throws ArgumentError if the flag already exists', () {
+ var parser = ArgParser();
+ parser.addFlag('foo');
+ throwsIllegalArg(() => parser.addFlag('foo'));
+ });
+
+ test('throws ArgumentError if the option already exists', () {
+ var parser = ArgParser();
+ parser.addOption('foo');
+ throwsIllegalArg(() => parser.addFlag('foo'));
+ });
+
+ test('throws ArgumentError if the abbreviation exists', () {
+ var parser = ArgParser();
+ parser.addFlag('foo', abbr: 'f');
+ throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'f'));
+ });
+
+ test(
+ 'throws ArgumentError if the abbreviation is longer '
+ 'than one character', () {
+ var parser = ArgParser();
+ throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu'));
+ });
+
+ test('throws ArgumentError if a flag name is invalid', () {
+ var parser = ArgParser();
+
+ for (var name in _invalidOptions) {
+ var reason = '${Error.safeToString(name)} is not valid';
+ throwsIllegalArg(() => parser.addFlag(name), reason: reason);
+ }
+ });
+
+ test('accepts valid flag names', () {
+ var parser = ArgParser();
+
+ for (var name in _validOptions) {
+ var reason = '${Error.safeToString(name)} is valid';
+ expect(() => parser.addFlag(name), returnsNormally, reason: reason);
+ }
+ });
+ });
+
+ group('ArgParser.addOption()', () {
+ test('throws ArgumentError if the flag already exists', () {
+ var parser = ArgParser();
+ parser.addFlag('foo');
+ throwsIllegalArg(() => parser.addOption('foo'));
+ });
+
+ test('throws ArgumentError if the option already exists', () {
+ var parser = ArgParser();
+ parser.addOption('foo');
+ throwsIllegalArg(() => parser.addOption('foo'));
+ });
+
+ test('throws ArgumentError if the abbreviation exists', () {
+ var parser = ArgParser();
+ parser.addFlag('foo', abbr: 'f');
+ throwsIllegalArg(() => parser.addOption('flummox', abbr: 'f'));
+ });
+
+ test(
+ 'throws ArgumentError if the abbreviation is longer '
+ 'than one character', () {
+ var parser = ArgParser();
+ throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu'));
+ });
+
+ test('throws ArgumentError if the abbreviation is empty', () {
+ var parser = ArgParser();
+ throwsIllegalArg(() => parser.addOption('flummox', abbr: ''));
+ });
+
+ test('throws ArgumentError if the abbreviation is an invalid value', () {
+ var parser = ArgParser();
+ for (var name in _invalidOptions) {
+ throwsIllegalArg(() => parser.addOption('flummox', abbr: name));
+ }
+ });
+
+ test('throws ArgumentError if the abbreviation is a dash', () {
+ var parser = ArgParser();
+ throwsIllegalArg(() => parser.addOption('flummox', abbr: '-'));
+ });
+
+ test('allows explict null value for "abbr"', () {
+ var parser = ArgParser();
+ expect(() => parser.addOption('flummox', abbr: null), returnsNormally);
+ });
+
+ test('throws ArgumentError if an option name is invalid', () {
+ var parser = ArgParser();
+
+ for (var name in _invalidOptions) {
+ var reason = '${Error.safeToString(name)} is not valid';
+ throwsIllegalArg(() => parser.addOption(name), reason: reason);
+ }
+ });
+
+ test('accepts valid option names', () {
+ var parser = ArgParser();
+
+ for (var name in _validOptions) {
+ var reason = '${Error.safeToString(name)} is valid';
+ expect(() => parser.addOption(name), returnsNormally, reason: reason);
+ }
+ });
+ });
+
+ group('ArgParser.getDefault()', () {
+ test('returns the default value for an option', () {
+ var parser = ArgParser();
+ parser.addOption('mode', defaultsTo: 'debug');
+ expect(parser.defaultFor('mode'), 'debug');
+ });
+
+ test('throws if the option is unknown', () {
+ var parser = ArgParser();
+ parser.addOption('mode', defaultsTo: 'debug');
+ throwsIllegalArg(() => parser.defaultFor('undefined'));
+ });
+ });
+
+ group('ArgParser.commands', () {
+ test('returns an empty map if there are no commands', () {
+ var parser = ArgParser();
+ expect(parser.commands, isEmpty);
+ });
+
+ test('returns the commands that were added', () {
+ var parser = ArgParser();
+ parser.addCommand('hide');
+ parser.addCommand('seek');
+ expect(parser.commands, hasLength(2));
+ expect(parser.commands['hide'], isNotNull);
+ expect(parser.commands['seek'], isNotNull);
+ });
+
+ test('iterates over the commands in the order they were added', () {
+ var parser = ArgParser();
+ parser.addCommand('a');
+ parser.addCommand('d');
+ parser.addCommand('b');
+ parser.addCommand('c');
+ expect(parser.commands.keys, equals(['a', 'd', 'b', 'c']));
+ });
+ });
+
+ group('ArgParser.options', () {
+ test('returns an empty map if there are no options', () {
+ var parser = ArgParser();
+ expect(parser.options, isEmpty);
+ });
+
+ test('returns the options that were added', () {
+ var parser = ArgParser();
+ parser.addFlag('hide');
+ parser.addOption('seek');
+ expect(parser.options, hasLength(2));
+ expect(parser.options['hide'], isNotNull);
+ expect(parser.options['seek'], isNotNull);
+ });
+
+ test('iterates over the options in the order they were added', () {
+ var parser = ArgParser();
+ parser.addFlag('a');
+ parser.addOption('d');
+ parser.addFlag('b');
+ parser.addOption('c');
+ expect(parser.options.keys, equals(['a', 'd', 'b', 'c']));
+ });
+ });
+
+ group('ArgParser.findByNameOrAlias', () {
+ test('returns null if there is no match', () {
+ var parser = ArgParser();
+ expect(parser.findByNameOrAlias('a'), isNull);
+ });
+
+ test('can find options by alias', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ expect(parser.findByNameOrAlias('b'),
+ isA<Option>().having((o) => o.name, 'name', 'a'));
+ });
+
+ test('can find flags by alias', () {
+ var parser = ArgParser()..addFlag('a', aliases: ['b']);
+ expect(parser.findByNameOrAlias('b'),
+ isA<Option>().having((o) => o.name, 'name', 'a'));
+ });
+
+ test('does not allow duplicate aliases', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ throwsIllegalArg(() => parser.addOption('c', aliases: ['b']));
+ });
+
+ test('does not allow aliases that conflict with existing names', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ throwsIllegalArg(() => parser.addOption('c', aliases: ['a']));
+ });
+
+ test('does not allow names that conflict with existing aliases', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ throwsIllegalArg(() => parser.addOption('b'));
+ });
+ });
+
+ group('ArgResults', () {
+ group('options', () {
+ test('returns the provided options', () {
+ var parser = ArgParser();
+ parser.addFlag('woof');
+ parser.addOption('meow');
+
+ parser.addOption('missing-option');
+ parser.addFlag('missing-flag', defaultsTo: null);
+
+ var args = parser.parse(['--woof', '--meow', 'kitty']);
+ expect(args.options, hasLength(2));
+ expect(args.options, contains('woof'));
+ expect(args.options, contains('meow'));
+ });
+
+ test('includes defaulted options', () {
+ var parser = ArgParser();
+ parser.addFlag('woof', defaultsTo: false);
+ parser.addOption('meow', defaultsTo: 'kitty');
+
+ // Flags normally have a default value.
+ parser.addFlag('moo');
+
+ parser.addOption('missing-option');
+ parser.addFlag('missing-flag', defaultsTo: null);
+
+ var args = parser.parse([]);
+ expect(args.options, hasLength(3));
+ expect(args.options, contains('woof'));
+ expect(args.options, contains('meow'));
+ expect(args.options, contains('moo'));
+ });
+ });
+
+ test('[] throws if the name is not an option', () {
+ var results = ArgParser().parse([]);
+ throwsIllegalArg(() => results['unknown']);
+ });
+
+ test('rest cannot be modified', () {
+ var results = ArgParser().parse([]);
+ expect(() => results.rest.add('oops'), throwsUnsupportedError);
+ });
+
+ test('.arguments returns the original argument list', () {
+ var parser = ArgParser();
+ parser.addFlag('foo');
+
+ var results = parser.parse(['--foo']);
+ expect(results.arguments, equals(['--foo']));
+ });
+
+ group('.wasParsed()', () {
+ test('throws if the name is not an option', () {
+ var results = ArgParser().parse([]);
+ throwsIllegalArg(() => results.wasParsed('unknown'));
+ });
+
+ test('returns true for parsed options', () {
+ var parser = ArgParser();
+ parser.addFlag('fast');
+ parser.addFlag('verbose');
+ parser.addOption('mode');
+ parser.addOption('output');
+
+ var results = parser.parse(['--fast', '--mode=debug']);
+
+ expect(results.wasParsed('fast'), isTrue);
+ expect(results.wasParsed('verbose'), isFalse);
+ expect(results.wasParsed('mode'), isTrue);
+ expect(results.wasParsed('output'), isFalse);
+ });
+ });
+ });
+
+ group('Option', () {
+ test('.valueOrDefault() returns a type-specific default value', () {
+ var parser = ArgParser();
+ parser.addFlag('flag-no', defaultsTo: null);
+ parser.addFlag('flag-def', defaultsTo: true);
+ parser.addOption('single-no');
+ parser.addOption('single-def', defaultsTo: 'def');
+ parser.addMultiOption('multi-no');
+ parser.addMultiOption('multi-def', defaultsTo: ['def']);
+
+ expect(parser.options['flag-no']!.valueOrDefault(null), equals(null));
+ expect(parser.options['flag-no']!.valueOrDefault(false), equals(false));
+ expect(parser.options['flag-def']!.valueOrDefault(null), equals(true));
+ expect(parser.options['flag-def']!.valueOrDefault(false), equals(false));
+ expect(parser.options['single-no']!.valueOrDefault(null), equals(null));
+ expect(parser.options['single-no']!.valueOrDefault('v'), equals('v'));
+ expect(parser.options['single-def']!.valueOrDefault(null), equals('def'));
+ expect(parser.options['single-def']!.valueOrDefault('v'), equals('v'));
+ expect(parser.options['multi-no']!.valueOrDefault(null), equals([]));
+ expect(parser.options['multi-no']!.valueOrDefault(['v']), equals(['v']));
+ expect(
+ parser.options['multi-def']!.valueOrDefault(null), equals(['def']));
+ expect(parser.options['multi-def']!.valueOrDefault(['v']), equals(['v']));
+ });
+ });
+}
+
+const _invalidOptions = [
+ ' ',
+ '',
+ '-',
+ '--',
+ '--foo',
+ ' with space',
+ 'with\ttab',
+ 'with\rcarriage\rreturn',
+ 'with\nline\nfeed',
+ "'singlequotes'",
+ '"doublequotes"',
+ 'back\\slash',
+ 'forward/slash'
+];
+
+const _validOptions = [
+ 'a', // One character.
+ 'contains-dash',
+ 'contains_underscore',
+ 'ends-with-dash-',
+ 'contains--doubledash--',
+ '1starts-with-number',
+ 'contains-a-1number',
+ 'ends-with-a-number8'
+];
diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart
new file mode 100644
index 0000000..b6437c1
--- /dev/null
+++ b/pkgs/args/test/command_parse_test.dart
@@ -0,0 +1,204 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ group('ArgParser.addCommand()', () {
+ test('creates a new ArgParser if none is given', () {
+ var parser = ArgParser();
+ var command = parser.addCommand('install');
+ expect(parser.commands['install'], equals(command));
+ expect(command, const TypeMatcher<ArgParser>());
+ });
+
+ test('uses the command parser if given one', () {
+ var parser = ArgParser();
+ var command = ArgParser();
+ var result = parser.addCommand('install', command);
+ expect(parser.commands['install'], equals(command));
+ expect(result, equals(command));
+ });
+
+ test('throws on a duplicate command name', () {
+ var parser = ArgParser();
+ parser.addCommand('install');
+ throwsIllegalArg(() => parser.addCommand('install'));
+ });
+ });
+
+ group('ArgParser.parse()', () {
+ test('parses a command', () {
+ var parser = ArgParser()..addCommand('install');
+
+ var args = parser.parse(['install']);
+
+ expect(args.command!.name, equals('install'));
+ expect(args.rest, isEmpty);
+ });
+
+ test('parses a command option', () {
+ var parser = ArgParser();
+ var command = parser.addCommand('install');
+ command.addOption('path');
+
+ var args = parser.parse(['install', '--path', 'some/path']);
+ expect(args.command!['path'], equals('some/path'));
+ });
+
+ test('parses a parent solo option before the command', () {
+ var parser = ArgParser()
+ ..addOption('mode', abbr: 'm')
+ ..addCommand('install');
+
+ var args = parser.parse(['-m', 'debug', 'install']);
+ expect(args['mode'], equals('debug'));
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent solo option after the command', () {
+ var parser = ArgParser()
+ ..addOption('mode', abbr: 'm')
+ ..addCommand('install');
+
+ var args = parser.parse(['install', '-m', 'debug']);
+ expect(args['mode'], equals('debug'));
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent option before the command', () {
+ var parser = ArgParser()
+ ..addFlag('verbose')
+ ..addCommand('install');
+
+ var args = parser.parse(['--verbose', 'install']);
+ expect(args['verbose'], isTrue);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent option after the command', () {
+ var parser = ArgParser()
+ ..addFlag('verbose')
+ ..addCommand('install');
+
+ var args = parser.parse(['install', '--verbose']);
+ expect(args['verbose'], isTrue);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent negated option before the command', () {
+ var parser = ArgParser()
+ ..addFlag('verbose', defaultsTo: true)
+ ..addCommand('install');
+
+ var args = parser.parse(['--no-verbose', 'install']);
+ expect(args['verbose'], isFalse);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent negated option after the command', () {
+ var parser = ArgParser()
+ ..addFlag('verbose', defaultsTo: true)
+ ..addCommand('install');
+
+ var args = parser.parse(['install', '--no-verbose']);
+ expect(args['verbose'], isFalse);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent abbreviation before the command', () {
+ var parser = ArgParser()
+ ..addFlag('debug', abbr: 'd')
+ ..addFlag('verbose', abbr: 'v')
+ ..addCommand('install');
+
+ var args = parser.parse(['-dv', 'install']);
+ expect(args['debug'], isTrue);
+ expect(args['verbose'], isTrue);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('parses a parent abbreviation after the command', () {
+ var parser = ArgParser()
+ ..addFlag('debug', abbr: 'd')
+ ..addFlag('verbose', abbr: 'v')
+ ..addCommand('install');
+
+ var args = parser.parse(['install', '-dv']);
+ expect(args['debug'], isTrue);
+ expect(args['verbose'], isTrue);
+ expect(args.command!.name, equals('install'));
+ });
+
+ test('does not parse a solo command option before the command', () {
+ var parser = ArgParser();
+ var command = parser.addCommand('install');
+ command.addOption('path', abbr: 'p');
+
+ throwsFormat(parser, ['-p', 'foo', 'install']);
+ });
+
+ test('does not parse a command option before the command', () {
+ var parser = ArgParser();
+ var command = parser.addCommand('install');
+ command.addOption('path');
+
+ throwsFormat(parser, ['--path', 'foo', 'install']);
+ });
+
+ test('does not parse a command abbreviation before the command', () {
+ var parser = ArgParser();
+ var command = parser.addCommand('install');
+ command.addFlag('debug', abbr: 'd');
+ command.addFlag('verbose', abbr: 'v');
+
+ throwsFormat(parser, ['-dv', 'install']);
+ });
+
+ test('assigns collapsed options to the proper command', () {
+ var parser = ArgParser();
+ parser.addFlag('apple', abbr: 'a');
+ var command = parser.addCommand('cmd');
+ command.addFlag('banana', abbr: 'b');
+ var subcommand = command.addCommand('subcmd');
+ subcommand.addFlag('cherry', abbr: 'c');
+
+ var args = parser.parse(['cmd', 'subcmd', '-abc']);
+ expect(args['apple'], isTrue);
+ expect(args.command!.name, equals('cmd'));
+ expect(args.command!['banana'], isTrue);
+ expect(args.command!.command!.name, equals('subcmd'));
+ expect(args.command!.command!['cherry'], isTrue);
+ });
+
+ test('option is given to innermost command that can take it', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+ parser.addCommand('cmd')
+ ..addFlag('verbose')
+ ..addCommand('subcmd');
+
+ var args = parser.parse(['cmd', 'subcmd', '--verbose']);
+ expect(args['verbose'], isFalse);
+ expect(args.command!.name, equals('cmd'));
+ expect(args.command!['verbose'], isTrue);
+ expect(args.command!.command!.name, equals('subcmd'));
+ });
+
+ test('remaining arguments are given to the innermost command', () {
+ var parser = ArgParser();
+ parser.addCommand('cmd').addCommand('subcmd');
+
+ var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']);
+ expect(args.command!.name, equals('cmd'));
+ expect(args.rest, isEmpty);
+ expect(args.command!.command!.name, equals('subcmd'));
+ expect(args.command!.rest, isEmpty);
+ expect(args.command!.command!.rest, equals(['other', 'stuff']));
+ });
+ });
+}
diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart
new file mode 100644
index 0000000..b9fde8a
--- /dev/null
+++ b/pkgs/args/test/command_runner_test.dart
@@ -0,0 +1,759 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/command_runner.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+const _defaultUsage = '''
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ help Display help information for test.
+
+Run "test help <command>" for more information about a command.''';
+
+void main() {
+ late CommandRunner runner;
+ setUp(() {
+ runner = CommandRunner('test', 'A test command runner.');
+ });
+
+ test('.invocation has a sane default', () {
+ expect(runner.invocation, equals('test <command> [arguments]'));
+ });
+
+ group('.usage', () {
+ test('returns the usage string', () {
+ expect(runner.usage, equals('''
+A test command runner.
+
+$_defaultUsage'''));
+ });
+
+ test('contains custom commands', () {
+ runner.addCommand(FooCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ foo Set a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ group('displays categories', () {
+ test('when some commands are categorized', () {
+ runner.addCommand(Category1Command());
+ runner.addCommand(Category2Command());
+ runner.addCommand(FooCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ foo Set a value.
+
+Displayers
+ baz Display a value.
+
+Printers
+ bar Print a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('except when all commands in a category are hidden', () {
+ runner.addCommand(Category1Command());
+ runner.addCommand(HiddenCategorizedCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+
+Printers
+ bar Print a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('when all commands are categorized', () {
+ runner.addCommand(Category1Command());
+ runner.addCommand(Category2Command());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+
+Displayers
+ baz Display a value.
+
+Printers
+ bar Print a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('when multiple commands are in a category', () {
+ runner.addCommand(Category1Command());
+ runner.addCommand(Category2Command());
+ runner.addCommand(Category2Command2());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+
+Displayers
+ baz Display a value.
+ baz2 Display another value.
+
+Printers
+ bar Print a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+ });
+
+ test('truncates newlines in command descriptions by default', () {
+ runner.addCommand(MultilineCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ multiline Multi
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('supports newlines in command summaries', () {
+ runner.addCommand(MultilineSummaryCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ multiline Multi
+ line.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('contains custom options', () {
+ runner.argParser.addFlag('foo', help: 'Do something.');
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+ --[no-]foo Do something.
+
+Available commands:
+ help Display help information for test.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test("doesn't print hidden commands", () {
+ runner
+ ..addCommand(HiddenCommand())
+ ..addCommand(HiddenCategorizedCommand())
+ ..addCommand(FooCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ foo Set a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test("doesn't print aliases", () {
+ runner.addCommand(AliasedCommand());
+
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ aliased Set a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test('respects usageLineLength', () {
+ runner = CommandRunner('name', 'desc', usageLineLength: 35);
+ expect(runner.usage, equals('''
+desc
+
+Usage: name <command> [arguments]
+
+Global options:
+-h, --help Print this usage
+ information.
+
+Available commands:
+ help Display help information
+ for name.
+
+Run "name help <command>" for more
+information about a command.'''));
+ });
+ });
+
+ test('usageException splits up the message and usage', () {
+ expect(() => runner.usageException('message'),
+ throwsUsageException('message', _defaultUsage));
+ });
+
+ group('run()', () {
+ test('runs a command', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ expect(
+ runner.run(['foo']).then((_) {
+ expect(command.hasRun, isTrue);
+ }),
+ completes);
+ });
+
+ test('runs an asynchronous command', () {
+ var command = AsyncCommand();
+ runner.addCommand(command);
+
+ expect(
+ runner.run(['async']).then((_) {
+ expect(command.hasRun, isTrue);
+ }),
+ completes);
+ });
+
+ test('runs a command with a return value', () {
+ var runner = CommandRunner<int>('test', '');
+ var command = ValueCommand();
+ runner.addCommand(command);
+
+ expect(runner.run(['foo']), completion(equals(12)));
+ });
+
+ test('runs a command with an asynchronous return value', () {
+ var runner = CommandRunner<String>('test', '');
+ var command = AsyncValueCommand();
+ runner.addCommand(command);
+
+ expect(runner.run(['foo']), completion(equals('hi')));
+ });
+
+ test('runs a hidden comand', () {
+ var command = HiddenCommand();
+ runner.addCommand(command);
+
+ expect(
+ runner.run(['hidden']).then((_) {
+ expect(command.hasRun, isTrue);
+ }),
+ completes);
+ });
+
+ test('runs an aliased comand', () {
+ var command = AliasedCommand();
+ runner.addCommand(command);
+
+ expect(
+ runner.run(['als']).then((_) {
+ expect(command.hasRun, isTrue);
+ }),
+ completes);
+ });
+
+ test('runs a subcommand', () {
+ var command = AsyncCommand();
+ runner.addCommand(FooCommand()..addSubcommand(command));
+
+ expect(
+ runner.run(['foo', 'async']).then((_) {
+ expect(command.hasRun, isTrue);
+ }),
+ completes);
+ });
+
+ group('suggests similar commands', () {
+ test('deletions', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['afoo', 'foao', 'fooa']) {
+ expect(() => runner.run([typo]), throwsUsageException('''
+Could not find a command named "$typo".
+
+Did you mean one of these?
+ foo
+''', anything));
+ }
+ });
+
+ test('additions', () {
+ var command = LongCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['ong', 'lng', 'lon']) {
+ expect(() => runner.run([typo]), throwsUsageException('''
+Could not find a command named "$typo".
+
+Did you mean one of these?
+ long
+''', anything));
+ }
+ });
+
+ test('substitutions', () {
+ var command = LongCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['aong', 'lang', 'lona']) {
+ expect(() => runner.run([typo]), throwsUsageException('''
+Could not find a command named "$typo".
+
+Did you mean one of these?
+ long
+''', anything));
+ }
+ });
+
+ test('swaps', () {
+ var command = LongCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['olng', 'lnog', 'logn']) {
+ expect(() => runner.run([typo]), throwsUsageException('''
+Could not find a command named "$typo".
+
+Did you mean one of these?
+ long
+''', anything));
+ }
+ });
+
+ test('combinations', () {
+ var command = LongCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['oln', 'on', 'lgn', 'alogn']) {
+ expect(() => runner.run([typo]), throwsUsageException('''
+Could not find a command named "$typo".
+
+Did you mean one of these?
+ long
+''', anything));
+ }
+ });
+
+ test('sorts by relevance', () {
+ var a = CustomNameCommand('abcd');
+ runner.addCommand(a);
+ var b = CustomNameCommand('bcd');
+ runner.addCommand(b);
+
+ expect(() => runner.run(['abdc']), throwsUsageException('''
+Could not find a command named "abdc".
+
+Did you mean one of these?
+ abcd
+ bcd
+''', anything));
+
+ expect(() => runner.run(['bdc']), throwsUsageException('''
+Could not find a command named "bdc".
+
+Did you mean one of these?
+ bcd
+ abcd
+''', anything));
+ });
+
+ test('omits commands with an edit distance over 2', () {
+ var command = LongCommand();
+ runner.addCommand(command);
+
+ for (var typo in ['llllong', 'aolgn', 'abcg', 'longggg']) {
+ expect(
+ () => runner.run([typo]),
+ throwsUsageException(
+ 'Could not find a command named "$typo".', anything));
+ }
+ });
+
+ test('max edit distance is configurable', () {
+ runner = CommandRunner('test', 'A test command runner.',
+ suggestionDistanceLimit: 1)
+ ..addCommand(LongCommand());
+ expect(
+ () => runner.run(['ng']),
+ throwsUsageException(
+ 'Could not find a command named "ng".', anything));
+
+ runner = CommandRunner('test', 'A test command runner.',
+ suggestionDistanceLimit: 3)
+ ..addCommand(LongCommand());
+ expect(() => runner.run(['g']), throwsUsageException('''
+Could not find a command named "g".
+
+Did you mean one of these?
+ long
+''', anything));
+ });
+
+ test('supports subcommands', () {
+ var command = FooCommand();
+ command.addSubcommand(LongCommand());
+ runner.addCommand(command);
+ expect(() => runner.run(['foo', 'ong']), throwsUsageException('''
+Could not find a subcommand named "ong" for "test foo".
+
+Did you mean one of these?
+ long
+''', anything));
+ });
+
+ test('doesn\'t show hidden commands', () {
+ var command = HiddenCommand();
+ runner.addCommand(command);
+ expect(
+ () => runner.run(['hidde']),
+ throwsUsageException(
+ 'Could not find a command named "hidde".', anything));
+ });
+
+ test('Suggests based on aliases', () {
+ var command = AliasedCommand();
+ runner.addCommand(command);
+ expect(() => runner.run(['rename']), throwsUsageException('''
+Could not find a command named "rename".
+
+Did you mean one of these?
+ aliased
+''', anything));
+ });
+
+ test('Suggests based on suggestedAliases', () {
+ var command = SuggestionAliasedCommand();
+ runner.addCommand(command);
+ expect(() => runner.run(['renamed']), throwsUsageException('''
+Could not find a command named "renamed".
+
+Did you mean one of these?
+ aliased
+''', anything));
+ });
+ });
+
+ group('with --help', () {
+ test('with no command prints the usage', () {
+ expect(() => runner.run(['--help']), prints('''
+A test command runner.
+
+$_defaultUsage
+'''));
+ });
+
+ test('with a preceding command prints the usage for that command', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ expect(() => runner.run(['foo', '--help']), prints('''
+Set a value.
+
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.
+'''));
+ });
+
+ test('with a following command prints the usage for that command', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ expect(() => runner.run(['--help', 'foo']), prints('''
+Set a value.
+
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.
+'''));
+ });
+ });
+
+ group('with help command', () {
+ test('with no command prints the usage', () {
+ expect(() => runner.run(['help']), prints('''
+A test command runner.
+
+$_defaultUsage
+'''));
+ });
+
+ test('with a command prints the usage for that command', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ expect(() => runner.run(['help', 'foo']), prints('''
+Set a value.
+
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.
+'''));
+ });
+
+ test('prints its own usage', () {
+ expect(() => runner.run(['help', 'help']), prints('''
+Display help information for test.
+
+Usage: test help [command]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.
+'''));
+ });
+ });
+
+ group('with an invalid argument', () {
+ test('at the root throws the root usage', () {
+ expect(
+ runner.run(['--asdf']),
+ throwsUsageException(
+ 'Could not find an option named "--asdf".', _defaultUsage));
+ });
+
+ test('for a command throws the command usage', () {
+ var command = FooCommand();
+ runner.addCommand(command);
+
+ expect(runner.run(['foo', '--asdf']),
+ throwsUsageException('Could not find an option named "--asdf".', '''
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.'''));
+ });
+ });
+ });
+
+ group('with a footer', () {
+ setUp(() {
+ runner = CommandRunnerWithFooter('test', 'A test command runner.');
+ });
+
+ test('includes the footer in the usage string', () {
+ expect(runner.usage, equals('''
+A test command runner.
+
+$_defaultUsage
+Also, footer!'''));
+ });
+
+ test('includes the footer in usage errors', () {
+ expect(
+ runner.run(['--bad']),
+ throwsUsageException('Could not find an option named "--bad".',
+ '$_defaultUsage\nAlso, footer!'));
+ });
+ });
+
+ group('with a footer and wrapping', () {
+ setUp(() {
+ runner =
+ CommandRunnerWithFooterAndWrapping('test', 'A test command runner.');
+ });
+ test('includes the footer in the usage string', () {
+ expect(runner.usage, equals('''
+A test command runner.
+
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage
+ information.
+
+Available commands:
+ help Display help information for
+ test.
+
+Run "test help <command>" for more
+information about a command.
+LONG footer! This is a long footer, so
+we can check wrapping on long footer
+messages.
+
+And make sure that they preserve
+newlines properly.'''));
+ });
+
+ test('includes the footer in usage errors', () {
+ expect(runner.run(['--bad']),
+ throwsUsageException('Could not find an option named "--bad".', '''
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage
+ information.
+
+Available commands:
+ help Display help information for
+ test.
+
+Run "test help <command>" for more
+information about a command.
+LONG footer! This is a long footer, so
+we can check wrapping on long footer
+messages.
+
+And make sure that they preserve
+newlines properly.'''));
+ });
+ });
+
+ group('throws a useful error when', () {
+ test('arg parsing fails', () {
+ expect(
+ runner.run(['--bad']),
+ throwsUsageException(
+ 'Could not find an option named "--bad".', _defaultUsage));
+ });
+
+ test("a top-level command doesn't exist", () {
+ expect(
+ runner.run(['bad']),
+ throwsUsageException(
+ 'Could not find a command named "bad".', _defaultUsage));
+ });
+
+ test("a subcommand doesn't exist", () {
+ runner.addCommand(FooCommand()..addSubcommand(AsyncCommand()));
+
+ expect(runner.run(['foo bad']),
+ throwsUsageException('Could not find a command named "foo bad".', '''
+Usage: test <command> [arguments]
+
+Global options:
+-h, --help Print this usage information.
+
+Available commands:
+ foo Set a value.
+
+Run "test help <command>" for more information about a command.'''));
+ });
+
+ test("a subcommand wasn't passed", () {
+ runner.addCommand(FooCommand()..addSubcommand(AsyncCommand()));
+
+ expect(runner.run(['foo']),
+ throwsUsageException('Missing subcommand for "test foo".', '''
+Usage: test foo <subcommand> [arguments]
+-h, --help Print this usage information.
+
+Available subcommands:
+ async Set a value asynchronously.
+
+Run "test help" to see global options.'''));
+ });
+
+ test("a command that doesn't take arguments was given them", () {
+ runner.addCommand(FooCommand());
+
+ expect(runner.run(['foo', 'bar']),
+ throwsUsageException('Command "foo" does not take any arguments.', '''
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.'''));
+ });
+ });
+
+ test('mandatory options in commands', () async {
+ var subcommand = _MandatoryOptionCommand();
+ runner.addCommand(subcommand);
+ expect(
+ () => runner.run([subcommand.name]),
+ throwsA(isA<ArgumentError>().having((e) => e.message, 'message',
+ contains('Option mandatory-option is mandatory'))));
+ expect(await runner.run([subcommand.name, '--mandatory-option', 'foo']),
+ 'foo');
+ });
+}
+
+class _MandatoryOptionCommand extends Command {
+ _MandatoryOptionCommand() {
+ argParser.addOption('mandatory-option', mandatory: true);
+ }
+
+ @override
+ String get description => 'A command with a mandatory option';
+
+ @override
+ String get name => 'mandatory-option-command';
+
+ @override
+ String run() => argResults!['mandatory-option'] as String;
+}
diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart
new file mode 100644
index 0000000..555cc8d
--- /dev/null
+++ b/pkgs/args/test/command_test.dart
@@ -0,0 +1,158 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/command_runner.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ late FooCommand foo;
+ setUp(() {
+ foo = FooCommand();
+
+ // Make sure [Command.runner] is set up.
+ CommandRunner<void>('test', 'A test command runner.').addCommand(foo);
+ });
+
+ group('.invocation has a sane default', () {
+ test('without subcommands', () {
+ expect(foo.invocation, equals('test foo [arguments]'));
+ });
+
+ test('with subcommands', () {
+ foo.addSubcommand(AsyncCommand());
+ expect(foo.invocation, equals('test foo <subcommand> [arguments]'));
+ });
+
+ test('for a subcommand', () {
+ var async = AsyncCommand();
+ foo.addSubcommand(async);
+
+ expect(async.invocation, equals('test foo async [arguments]'));
+ });
+ });
+
+ group('.usage', () {
+ test('returns the usage string', () {
+ expect(foo.usage, equals('''
+Set a value.
+
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.'''));
+ });
+
+ test('contains custom options', () {
+ foo.argParser.addFlag('flag', help: 'Do something.');
+
+ expect(foo.usage, equals('''
+Set a value.
+
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+ --[no-]flag Do something.
+
+Run "test help" to see global options.'''));
+ });
+
+ test("doesn't print hidden subcommands", () {
+ foo.addSubcommand(AsyncCommand());
+ foo.addSubcommand(HiddenCommand());
+
+ expect(foo.usage, equals('''
+Set a value.
+
+Usage: test foo <subcommand> [arguments]
+-h, --help Print this usage information.
+
+Available subcommands:
+ async Set a value asynchronously.
+
+Run "test help" to see global options.'''));
+ });
+
+ test("doesn't print subcommand aliases", () {
+ foo.addSubcommand(AliasedCommand());
+
+ expect(foo.usage, equals('''
+Set a value.
+
+Usage: test foo <subcommand> [arguments]
+-h, --help Print this usage information.
+
+Available subcommands:
+ aliased Set a value.
+
+Run "test help" to see global options.'''));
+ });
+
+ test('wraps long command descriptions with subcommands', () {
+ var wrapping = WrappingCommand();
+
+ // Make sure [Command.runner] is set up.
+ CommandRunner<void>('longtest', 'A long-lined test command runner.')
+ .addCommand(wrapping);
+
+ wrapping.addSubcommand(LongCommand());
+ expect(wrapping.usage, equals('''
+This command overrides the argParser so
+that it will wrap long lines.
+
+Usage: longtest wrapping <subcommand>
+ [arguments]
+-h, --help Print this usage
+ information.
+
+Available subcommands:
+ long This command has a long
+ description that needs to be
+ wrapped sometimes.
+
+Run "longtest help" to see global
+options.'''));
+ });
+
+ test('wraps long command descriptions', () {
+ var longCommand = LongCommand();
+
+ // Make sure [Command.runner] is set up.
+ CommandRunner<void>('longtest', 'A long-lined test command runner.')
+ .addCommand(longCommand);
+
+ expect(longCommand.usage, equals('''
+This command has a long description that
+needs to be wrapped sometimes.
+It has embedded newlines,
+ and indented lines that also need
+ to be wrapped and have their
+ indentation preserved.
+0123456789012345678901234567890123456789
+0123456789012345678901234567890123456789
+01234567890123456789
+
+Usage: longtest long [arguments]
+-h, --help Print this usage
+ information.
+
+Run "longtest help" to see global
+options.'''));
+ });
+ });
+
+ test('usageException splits up the message and usage', () {
+ expect(
+ () => foo.usageException('message'), throwsUsageException('message', '''
+Usage: test foo [arguments]
+-h, --help Print this usage information.
+
+Run "test help" to see global options.'''));
+ });
+
+ test('considers a command hidden if all its subcommands are hidden', () {
+ foo.addSubcommand(HiddenCommand());
+ expect(foo.hidden, isTrue);
+ });
+}
diff --git a/pkgs/args/test/parse_performance_test.dart b/pkgs/args/test/parse_performance_test.dart
new file mode 100644
index 0000000..b099379
--- /dev/null
+++ b/pkgs/args/test/parse_performance_test.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('ArgParser.parse() is fast', () {
+ test('for short flags', () {
+ _testParserPerformance(ArgParser()..addFlag('short', abbr: 's'), '-s');
+ });
+
+ test('for abbreviations', () {
+ _testParserPerformance(
+ ArgParser()
+ ..addFlag('short', abbr: 's')
+ ..addFlag('short2', abbr: 't')
+ ..addFlag('short3', abbr: 'u')
+ ..addFlag('short4', abbr: 'v'),
+ '-stuv');
+ });
+
+ test('for long flags', () {
+ _testParserPerformance(ArgParser()..addFlag('long-flag'), '--long-flag');
+ });
+
+ test('for long options with =', () {
+ _testParserPerformance(ArgParser()..addOption('long-option-name'),
+ '--long-option-name=long-option-value');
+ });
+ });
+}
+
+/// Tests how quickly [parser] parses [string].
+///
+/// Checks that a 10x increase in arg count does not lead to greater than 30x
+/// increase in parse time.
+void _testParserPerformance(ArgParser parser, String string) {
+ var baseSize = 50000;
+ var baseList = List<String>.generate(baseSize, (_) => string);
+
+ var multiplier = 10;
+ var largeList = List<String>.generate(baseSize * multiplier, (_) => string);
+
+ ArgResults baseAction() => parser.parse(baseList);
+ ArgResults largeAction() => parser.parse(largeList);
+
+ // Warm up JIT.
+ baseAction();
+ largeAction();
+
+ var baseTime = _time(baseAction);
+ var largeTime = _time(largeAction);
+
+ print('Parsed $baseSize elements in ${baseTime}ms, '
+ '${baseSize * multiplier} elements in ${largeTime}ms.');
+
+ expect(
+ largeTime,
+ lessThan(baseTime * multiplier * 3),
+ reason:
+ 'Comparing large data set time ${largeTime}ms to small data set time '
+ '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to '
+ 'increase up to ${multiplier * 3}x, but it increased '
+ '${largeTime ~/ baseTime}x.',
+ );
+}
+
+int _time(void Function() function) {
+ var stopwatch = Stopwatch()..start();
+ function();
+ return stopwatch.elapsedMilliseconds;
+}
diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart
new file mode 100644
index 0000000..9501b5d
--- /dev/null
+++ b/pkgs/args/test/parse_test.dart
@@ -0,0 +1,819 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ group('ArgParser.parse()', () {
+ test('does not destructively modify the argument list', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = ['--verbose'];
+ var results = parser.parse(args);
+ expect(args, equals(['--verbose']));
+ expect(results['verbose'], isTrue);
+ });
+
+ group('flags', () {
+ test('are true if present', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = parser.parse(['--verbose']);
+ expect(args['verbose'], isTrue);
+ });
+
+ test('default if missing', () {
+ var parser = ArgParser();
+ parser.addFlag('a', defaultsTo: true);
+ parser.addFlag('b', defaultsTo: false);
+
+ var args = parser.parse([]);
+ expect(args['a'], isTrue);
+ expect(args['b'], isFalse);
+ });
+
+ test('are false if missing with no default', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = parser.parse([]);
+ expect(args['verbose'], isFalse);
+ });
+
+ test('throws if given a value', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ throwsFormat(parser, ['--verbose=true']);
+ });
+
+ test('are case-sensitive', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+ parser.addFlag('Verbose');
+ var results = parser.parse(['--verbose']);
+ expect(results['verbose'], isTrue);
+ expect(results['Verbose'], isFalse);
+ });
+
+ test('match letters, numbers, hyphens and underscores', () {
+ var parser = ArgParser();
+ var allCharacters =
+ 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789';
+ parser.addFlag(allCharacters);
+ var results = parser.parse(['--$allCharacters']);
+ expect(results[allCharacters], isTrue);
+ });
+
+ test('can match by alias', () {
+ var parser = ArgParser()..addFlag('a', aliases: ['b']);
+ var results = parser.parse(['--b']);
+ expect(results['a'], isTrue);
+ });
+
+ test('can be negated by alias', () {
+ var parser = ArgParser()
+ ..addFlag('a', aliases: ['b'], defaultsTo: true, negatable: true);
+ var results = parser.parse(['--no-b']);
+ expect(results['a'], isFalse);
+ });
+
+ test('throws if requested as a multi-option', () {
+ var parser = ArgParser();
+ parser.addFlag('a', defaultsTo: true);
+ var results = parser.parse(['--a']);
+ throwsIllegalArg(() => results.multiOption('a'));
+ });
+ });
+
+ group('flag()', () {
+ test('returns true if present', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = parser.parse(['--verbose']);
+ expect(args.flag('verbose'), isTrue);
+ });
+
+ test('returns default if missing', () {
+ var parser = ArgParser();
+ parser.addFlag('a', defaultsTo: true);
+ parser.addFlag('b', defaultsTo: false);
+
+ var args = parser.parse([]);
+ expect(args.flag('a'), isTrue);
+ expect(args.flag('b'), isFalse);
+ });
+
+ test('are false if missing with no default', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = parser.parse([]);
+ expect(args.flag('verbose'), isFalse);
+ });
+
+ test('are case-sensitive', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+ parser.addFlag('Verbose');
+ var results = parser.parse(['--verbose']);
+ expect(results.flag('verbose'), isTrue);
+ expect(results.flag('Verbose'), isFalse);
+ });
+
+ test('match letters, numbers, hyphens and underscores', () {
+ var parser = ArgParser();
+ var allCharacters =
+ 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789';
+ parser.addFlag(allCharacters);
+ var results = parser.parse(['--$allCharacters']);
+ expect(results.flag(allCharacters), isTrue);
+ });
+
+ test('can match by alias', () {
+ var parser = ArgParser()..addFlag('a', aliases: ['b']);
+ var results = parser.parse(['--b']);
+ expect(results.flag('a'), isTrue);
+ });
+
+ test('can be negated by alias', () {
+ var parser = ArgParser()
+ ..addFlag('a', aliases: ['b'], defaultsTo: true, negatable: true);
+ var results = parser.parse(['--no-b']);
+ expect(results.flag('a'), isFalse);
+ });
+
+ test('throws if requested as a multi-option', () {
+ var parser = ArgParser();
+ parser.addFlag('a', defaultsTo: true);
+ var results = parser.parse(['--a']);
+ throwsIllegalArg(() => results.multiOption('a'));
+ });
+ });
+
+ group('flags negated with "no-"', () {
+ test('set the flag to false', () {
+ var parser = ArgParser();
+ parser.addFlag('verbose');
+
+ var args = parser.parse(['--no-verbose']);
+ expect(args['verbose'], isFalse);
+ });
+
+ test('set the flag to true if the flag actually starts with "no-"', () {
+ var parser = ArgParser();
+ parser.addFlag('no-body');
+
+ var args = parser.parse(['--no-body']);
+ expect(args['no-body'], isTrue);
+ });
+
+ test('are not preferred over a colliding one without', () {
+ var parser = ArgParser();
+ parser.addFlag('no-strum');
+ parser.addFlag('strum');
+
+ var args = parser.parse(['--no-strum']);
+ expect(args['no-strum'], isTrue);
+ expect(args['strum'], isFalse);
+ });
+
+ test('fail for non-negatable flags', () {
+ var parser = ArgParser();
+ parser.addFlag('strum', negatable: false);
+
+ throwsFormat(parser, ['--no-strum']);
+ });
+ });
+
+ group('callbacks', () {
+ test('for present flags are invoked with the value', () {
+ bool? a;
+ var parser = ArgParser();
+ parser.addFlag('a', callback: (value) => a = value);
+
+ parser.parse(['--a']);
+ expect(a, isTrue);
+ });
+
+ test('for absent flags are invoked with the default value', () {
+ bool? a;
+ var parser = ArgParser();
+ parser.addFlag('a', defaultsTo: false, callback: (value) => a = value);
+
+ parser.parse([]);
+ expect(a, isFalse);
+ });
+
+ test('are invoked even if the flag is not present', () {
+ var a = true;
+ var parser = ArgParser();
+ parser.addFlag('a', callback: (value) => a = value);
+
+ parser.parse([]);
+ expect(a, isFalse);
+ });
+
+ test('for present options are invoked with the value', () {
+ String? a;
+ var parser = ArgParser();
+ parser.addOption('a', callback: (value) => a = value);
+
+ parser.parse(['--a=v']);
+ expect(a, equals('v'));
+ });
+
+ test('for absent options are invoked with the default value', () {
+ var parser = ArgParser();
+ parser.addOption('a',
+ defaultsTo: 'v',
+ callback: expectAsync1((value) => expect(value, 'v')));
+
+ parser.parse([]);
+ });
+
+ test('for absent options are invoked with null if there is no default',
+ () {
+ var parser = ArgParser();
+ parser.addOption('a',
+ callback: expectAsync1((value) => expect(value, isNull)));
+
+ parser.parse([]);
+ });
+
+ group('with addMultiOption', () {
+ test('for multiple present, options are invoked with value as a list',
+ () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a', callback: (value) => a = value);
+
+ parser.parse(['--a=v', '--a=x']);
+ expect(a, equals(['v', 'x']));
+
+ // This reified type is important in strong mode so that people can
+ // safely write "as List<String>".
+ expect(a, isA<List<String>>());
+ });
+
+ test(
+ 'for single present, options are invoked with value as a single '
+ 'element list', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a', callback: (value) => a = value);
+
+ parser.parse(['--a=v']);
+ expect(a, equals(['v']));
+ });
+
+ test('for absent, options are invoked with default value', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a',
+ defaultsTo: ['v', 'w'], callback: (value) => a = value);
+
+ parser.parse([]);
+ expect(a, equals(['v', 'w']));
+ });
+
+ test('for absent, options are invoked with value as an empty list', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a', callback: (value) => a = value);
+
+ parser.parse([]);
+ expect(a, isEmpty);
+ });
+
+ test('parses comma-separated strings', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a', callback: (value) => a = value);
+
+ parser.parse(['--a=v,w', '--a=x']);
+ expect(a, equals(['v', 'w', 'x']));
+ });
+
+ test("doesn't parse comma-separated strings with splitCommas: false",
+ () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a',
+ splitCommas: false, callback: (value) => a = value);
+
+ parser.parse(['--a=v,w', '--a=x']);
+ expect(a, equals(['v,w', 'x']));
+ });
+
+ test('parses empty strings', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a', callback: (value) => a = value);
+
+ parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']);
+ expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', '']));
+ });
+
+ test('with allowed parses comma-separated strings', () {
+ List<String>? a;
+ var parser = ArgParser();
+ parser.addMultiOption('a',
+ allowed: ['v', 'w', 'x'], callback: (value) => a = value);
+
+ parser.parse(['--a=v,w', '--a=x']);
+ expect(a, equals(['v', 'w', 'x']));
+ });
+
+ test('can mix and match alias and regular name', () {
+ var parser = ArgParser()..addMultiOption('a', aliases: ['b']);
+ var results = parser.parse(['--a=1', '--b=2']);
+ expect(results['a'], ['1', '2']);
+ });
+ });
+ });
+
+ group('abbreviations', () {
+ test('are parsed with a preceding "-"', () {
+ var parser = ArgParser();
+ parser.addFlag('arg', abbr: 'a');
+
+ var args = parser.parse(['-a']);
+ expect(args['arg'], isTrue);
+ });
+
+ test('can use multiple after a single "-"', () {
+ var parser = ArgParser();
+ parser.addFlag('first', abbr: 'f');
+ parser.addFlag('second', abbr: 's');
+ parser.addFlag('third', abbr: 't');
+
+ var args = parser.parse(['-tf']);
+ expect(args['first'], isTrue);
+ expect(args['second'], isFalse);
+ expect(args['third'], isTrue);
+ });
+
+ test('can have multiple "-" args', () {
+ var parser = ArgParser();
+ parser.addFlag('first', abbr: 'f');
+ parser.addFlag('second', abbr: 's');
+ parser.addFlag('third', abbr: 't');
+
+ var args = parser.parse(['-s', '-tf']);
+ expect(args['first'], isTrue);
+ expect(args['second'], isTrue);
+ expect(args['third'], isTrue);
+ });
+
+ test('can take arguments without a space separating', () {
+ var parser = ArgParser();
+ parser.addOption('file', abbr: 'f');
+
+ var args = parser.parse(['-flip']);
+ expect(args['file'], equals('lip'));
+ });
+
+ test('can take arguments with a space separating', () {
+ var parser = ArgParser();
+ parser.addOption('file', abbr: 'f');
+
+ var args = parser.parse(['-f', 'name']);
+ expect(args['file'], equals('name'));
+ });
+
+ test('allow non-option characters in the value', () {
+ var parser = ArgParser();
+ parser.addOption('apple', abbr: 'a');
+
+ var args = parser.parse(['-ab?!c']);
+ expect(args['apple'], equals('b?!c'));
+ });
+
+ test('throw if unknown', () {
+ var parser = ArgParser();
+ throwsFormat(parser, ['-f']);
+ });
+
+ test('throw if the value is missing', () {
+ var parser = ArgParser();
+ parser.addOption('file', abbr: 'f');
+
+ throwsFormat(parser, ['-f']);
+ });
+
+ test('does not throw if the value looks like an option', () {
+ var parser = ArgParser();
+ parser.addOption('file', abbr: 'f');
+ parser.addOption('other');
+
+ expect(parser.parse(['-f', '--other'])['file'], equals('--other'));
+ expect(parser.parse(['-f', '--unknown'])['file'], equals('--unknown'));
+ expect(parser.parse(['-f', '-abbr'])['file'], equals('-abbr'));
+ expect(parser.parse(['-f', '--'])['file'], equals('--'));
+ });
+
+ test('throw if the value is not allowed', () {
+ var parser = ArgParser();
+ parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']);
+
+ throwsFormat(parser, ['-mprofile']);
+ });
+
+ group('throw if a comma-separated value is not allowed', () {
+ test('with addMultiOption', () {
+ var parser = ArgParser();
+ parser
+ .addMultiOption('mode', abbr: 'm', allowed: ['debug', 'release']);
+
+ throwsFormat(parser, ['-mdebug,profile']);
+ });
+ });
+
+ test('throw if any but the first is not a flag', () {
+ var parser = ArgParser();
+ parser.addFlag('apple', abbr: 'a');
+ parser.addOption('banana', abbr: 'b'); // Takes an argument.
+ parser.addFlag('cherry', abbr: 'c');
+
+ throwsFormat(parser, ['-abc']);
+ });
+
+ test('throw if it has a value but the option is a flag', () {
+ var parser = ArgParser();
+ parser.addFlag('apple', abbr: 'a');
+ parser.addFlag('banana', abbr: 'b');
+
+ // The '?!' means this can only be understood as '--apple b?!c'.
+ throwsFormat(parser, ['-ab?!c']);
+ });
+
+ test('are case-sensitive', () {
+ var parser = ArgParser();
+ parser.addFlag('file', abbr: 'f');
+ parser.addFlag('force', abbr: 'F');
+ var results = parser.parse(['-f']);
+ expect(results['file'], isTrue);
+ expect(results['force'], isFalse);
+ });
+ });
+
+ group('options', () {
+ test('are parsed if present', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse(['--mode=release']);
+ expect(args['mode'], equals('release'));
+ });
+
+ test('are null if not present', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse([]);
+ expect(args['mode'], isNull);
+ });
+
+ test('default if missing', () {
+ var parser = ArgParser();
+ parser.addOption('mode', defaultsTo: 'debug');
+ var args = parser.parse([]);
+ expect(args['mode'], equals('debug'));
+ });
+
+ test('allow the value to be separated by whitespace', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse(['--mode', 'release']);
+ expect(args['mode'], equals('release'));
+ });
+
+ test('throw if unknown', () {
+ var parser = ArgParser();
+ throwsFormat(parser, ['--unknown']);
+ throwsFormat(parser, ['--nobody']); // Starts with "no".
+ });
+
+ test('throw if the arg does not include a value', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ throwsFormat(parser, ['--mode']);
+ });
+
+ test('do not throw if the value looks like an option', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ parser.addOption('other');
+
+ expect(parser.parse(['--mode', '--other'])['mode'], equals('--other'));
+ expect(
+ parser.parse(['--mode', '--unknown'])['mode'], equals('--unknown'));
+ expect(parser.parse(['--mode', '-abbr'])['mode'], equals('-abbr'));
+ expect(parser.parse(['--mode', '--'])['mode'], equals('--'));
+ });
+
+ test('do not throw if the value is in the allowed set', () {
+ var parser = ArgParser();
+ parser.addOption('mode', allowed: ['debug', 'release']);
+ var args = parser.parse(['--mode=debug']);
+ expect(args['mode'], equals('debug'));
+ });
+
+ test('do not throw if there is no allowed set with allowedHelp', () {
+ var parser = ArgParser();
+ parser.addOption('mode', allowedHelp: {
+ 'debug': 'During development.',
+ 'release': 'For customers.'
+ });
+ var args = parser.parse(['--mode=profile']);
+ expect(args['mode'], equals('profile'));
+ });
+
+ test('throw if the value is not in the allowed set', () {
+ var parser = ArgParser();
+ parser.addOption('mode', allowed: ['debug', 'release']);
+ throwsFormat(parser, ['--mode=profile']);
+ });
+
+ test('returns last provided value', () {
+ var parser = ArgParser();
+ parser.addOption('define');
+ var args = parser.parse(['--define=1', '--define=2']);
+ expect(args['define'], equals('2'));
+ });
+
+ test('throw if requested as a multi-option', () {
+ var parser = ArgParser();
+ parser.addOption('a', defaultsTo: 'b');
+ var results = parser.parse(['--a=c']);
+ throwsIllegalArg(() => results.multiOption('a'));
+ });
+
+ group('returns a List', () {
+ test('with addMultiOption', () {
+ var parser = ArgParser();
+ parser.addMultiOption('define');
+ var args = parser.parse(['--define=1']);
+ expect(args['define'], equals(['1']));
+ args = parser.parse(['--define=1', '--define=2']);
+ expect(args['define'], equals(['1', '2']));
+ });
+ });
+
+ group('returns the default value if not explicitly set', () {
+ test('with addMultiOption', () {
+ var parser = ArgParser();
+ parser.addMultiOption('define', defaultsTo: ['0']);
+ var args = parser.parse(['']);
+ expect(args['define'], equals(['0']));
+ });
+ });
+
+ test('are case-sensitive', () {
+ var parser = ArgParser();
+ parser.addOption('verbose', defaultsTo: 'no');
+ parser.addOption('Verbose', defaultsTo: 'no');
+ var results = parser.parse(['--verbose', 'chatty']);
+ expect(results['verbose'], equals('chatty'));
+ expect(results['Verbose'], equals('no'));
+ });
+
+ test('can be set by alias', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ var results = parser.parse(['--b=1']);
+ expect(results['a'], '1');
+ });
+
+ group('mandatory', () {
+ test('throw if no args', () {
+ var parser = ArgParser();
+ parser.addOption('username', mandatory: true);
+ var results = parser.parse([]);
+ expect(() => results['username'], throwsA(isA<ArgumentError>()));
+ });
+
+ test('throw if no mandatory args', () {
+ var parser = ArgParser();
+ parser.addOption('test');
+ parser.addOption('username', mandatory: true);
+ var results = parser.parse(['--test', 'test']);
+ expect(results['test'], equals('test'));
+ expect(() => results['username'], throwsA(isA<ArgumentError>()));
+ });
+
+ test('parse successfully', () {
+ var parser = ArgParser();
+ parser.addOption('test', mandatory: true);
+ var results = parser.parse(['--test', 'test']);
+ expect(results['test'], equals('test'));
+ });
+
+ test('throws when value retrieved', () {
+ var parser = ArgParser();
+ parser.addFlag('help', abbr: 'h', negatable: false);
+ parser.addOption('test', mandatory: true);
+ var results = parser.parse(['-h']);
+ expect(results['help'], true);
+ expect(() => results['test'], throwsA(isA<ArgumentError>()));
+ });
+ });
+ });
+
+ group('option()', () {
+ test('are parsed if present', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse(['--mode=release']);
+ expect(args.option('mode'), equals('release'));
+ });
+
+ test('are null if not present', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse([]);
+ expect(args.option('mode'), isNull);
+ });
+
+ test('default if missing', () {
+ var parser = ArgParser();
+ parser.addOption('mode', defaultsTo: 'debug');
+ var args = parser.parse([]);
+ expect(args.option('mode'), equals('debug'));
+ });
+
+ test('allow the value to be separated by whitespace', () {
+ var parser = ArgParser();
+ parser.addOption('mode');
+ var args = parser.parse(['--mode', 'release']);
+ expect(args.option('mode'), equals('release'));
+ });
+
+ test('do not throw if the value is in the allowed set', () {
+ var parser = ArgParser();
+ parser.addOption('mode', allowed: ['debug', 'release']);
+ var args = parser.parse(['--mode=debug']);
+ expect(args.option('mode'), equals('debug'));
+ });
+
+ test('do not throw if there is no allowed set with allowedHelp', () {
+ var parser = ArgParser();
+ parser.addOption('mode', allowedHelp: {
+ 'debug': 'During development.',
+ 'release': 'For customers.'
+ });
+ var args = parser.parse(['--mode=profile']);
+ expect(args.option('mode'), equals('profile'));
+ });
+
+ test('returns last provided value', () {
+ var parser = ArgParser();
+ parser.addOption('define');
+ var args = parser.parse(['--define=1', '--define=2']);
+ expect(args.option('define'), equals('2'));
+ });
+
+ test('throw if requested as a multi-option', () {
+ var parser = ArgParser();
+ parser.addOption('a', defaultsTo: 'b');
+ var results = parser.parse(['--a=c']);
+ throwsIllegalArg(() => results.multiOption('a'));
+ });
+
+ group('returns a List', () {
+ test('with addMultiOption', () {
+ var parser = ArgParser();
+ parser.addMultiOption('define');
+ var args = parser.parse(['--define=1']);
+ expect(args.multiOption('define'), equals(['1']));
+ args = parser.parse(['--define=1', '--define=2']);
+ expect(args.multiOption('define'), equals(['1', '2']));
+ });
+ });
+
+ group('returns the default value if not explicitly set', () {
+ test('with addMultiOption', () {
+ var parser = ArgParser();
+ parser.addMultiOption('define', defaultsTo: ['0']);
+ var args = parser.parse(['']);
+ expect(args.multiOption('define'), equals(['0']));
+ });
+ });
+
+ test('are case-sensitive', () {
+ var parser = ArgParser();
+ parser.addOption('verbose', defaultsTo: 'no');
+ parser.addOption('Verbose', defaultsTo: 'no');
+ var results = parser.parse(['--verbose', 'chatty']);
+ expect(results.option('verbose'), equals('chatty'));
+ expect(results.option('Verbose'), equals('no'));
+ });
+
+ test('can be set by alias', () {
+ var parser = ArgParser()..addOption('a', aliases: ['b']);
+ var results = parser.parse(['--b=1']);
+ expect(results.option('a'), '1');
+ });
+
+ group('mandatory', () {
+ test('parse successfully', () {
+ var parser = ArgParser();
+ parser.addOption('test', mandatory: true);
+ var results = parser.parse(['--test', 'test']);
+ expect(results.option('test'), equals('test'));
+ });
+ });
+ });
+
+ group('remaining args', () {
+ test('stops parsing args when a non-option-like arg is encountered', () {
+ var parser = ArgParser();
+ parser.addFlag('woof');
+ parser.addOption('meow');
+ parser.addOption('tweet', defaultsTo: 'bird');
+
+ var results = parser.parse(['--woof', '--meow', 'v', 'not', 'option']);
+ expect(results['woof'], isTrue);
+ expect(results['meow'], equals('v'));
+ expect(results['tweet'], equals('bird'));
+ expect(results.rest, equals(['not', 'option']));
+ });
+
+ test('consumes "--" and stops', () {
+ var parser = ArgParser();
+ parser.addFlag('woof', defaultsTo: false);
+ parser.addOption('meow', defaultsTo: 'kitty');
+
+ var results = parser.parse(['--woof', '--', '--meow']);
+ expect(results['woof'], isTrue);
+ expect(results['meow'], equals('kitty'));
+ expect(results.rest, equals(['--meow']));
+ });
+
+ test(
+ 'with allowTrailingOptions: false, leaves "--" if not the first '
+ 'non-option', () {
+ var parser = ArgParser(allowTrailingOptions: false);
+ parser.addFlag('woof');
+
+ var results = parser.parse(['--woof', 'stop', '--', 'arg']);
+ expect(results['woof'], isTrue);
+ expect(results.rest, equals(['stop', '--', 'arg']));
+ });
+ });
+
+ group('ArgParser Exception Tests', () {
+ test('throws exception for unknown option', () {
+ var parser = ArgParser();
+ throwsArgParserException(parser, ['--verbose'],
+ 'Could not find an option named "--verbose".', [], '--verbose');
+ throwsArgParserException(
+ parser, ['-v'], 'Could not find an option or flag "-v".', [], '-v');
+ });
+
+ test('throws exception for flag with value', () {
+ var parser = ArgParser();
+ parser.addFlag('flag', abbr: 'f');
+ throwsArgParserException(parser, ['--flag=1'],
+ 'Flag option "--flag" should not be given a value.', [], '--flag');
+ throwsArgParserException(parser, ['-f=1'],
+ 'Option "-f" is a flag and cannot handle value "=1".', [], '-f');
+ });
+
+ test('throws exception after parsing multiple options', () {
+ var parser = ArgParser();
+ parser.addOption('first');
+ parser.addOption('second');
+ throwsArgParserException(
+ parser,
+ ['--first', '1', '--second', '2', '--verbose', '3'],
+ 'Could not find an option named "--verbose".',
+ [],
+ '--verbose');
+ });
+
+ test('throws exception for option with invalid value', () {
+ var parser = ArgParser();
+ parser.addOption('first', allowed: ['a', 'b']);
+ throwsArgParserException(parser, ['--first', 'c'],
+ '"c" is not an allowed value for option "--first".', [], '--first');
+ });
+
+ test('throws exception after parsing command', () {
+ var parser = ArgParser();
+ parser.addCommand('command', ArgParser());
+ throwsArgParserException(
+ parser,
+ ['command', '--verbose'],
+ 'Could not find an option named "--verbose".',
+ ['command'],
+ '--verbose');
+ });
+ });
+ });
+}
diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart
new file mode 100644
index 0000000..f7d8b8a
--- /dev/null
+++ b/pkgs/args/test/test_utils.dart
@@ -0,0 +1,368 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:args/command_runner.dart';
+import 'package:test/test.dart';
+
+class CommandRunnerWithFooter extends CommandRunner {
+ @override
+ String get usageFooter => 'Also, footer!';
+
+ CommandRunnerWithFooter(super.executableName, super.description);
+}
+
+class CommandRunnerWithFooterAndWrapping extends CommandRunner {
+ @override
+ String get usageFooter => 'LONG footer! '
+ 'This is a long footer, so we can check wrapping on long footer messages.'
+ '\n\n'
+ 'And make sure that they preserve newlines properly.';
+
+ @override
+ ArgParser get argParser => _argParser;
+ final _argParser = ArgParser(usageLineLength: 40);
+
+ CommandRunnerWithFooterAndWrapping(super.executableName, super.description);
+}
+
+class FooCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'foo';
+
+ @override
+ final description = 'Set a value.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class ValueCommand extends Command<int> {
+ @override
+ final name = 'foo';
+
+ @override
+ final description = 'Return a value.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ int run() => 12;
+}
+
+class AsyncValueCommand extends Command<String> {
+ @override
+ final name = 'foo';
+
+ @override
+ final description = 'Return a future.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ Future<String> run() async => 'hi';
+}
+
+class Category1Command extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'bar';
+
+ @override
+ final description = 'Print a value.';
+
+ @override
+ final category = 'Printers';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class Category2Command extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'baz';
+
+ @override
+ final description = 'Display a value.';
+
+ @override
+ final category = 'Displayers';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class Category2Command2 extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'baz2';
+
+ @override
+ final description = 'Display another value.';
+
+ @override
+ final category = 'Displayers';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class MultilineCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'multiline';
+
+ @override
+ final description = 'Multi\nline.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class WrappingCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ ArgParser get argParser => _argParser;
+ final _argParser = ArgParser(usageLineLength: 40);
+
+ @override
+ final name = 'wrapping';
+
+ @override
+ final description =
+ 'This command overrides the argParser so that it will wrap long lines.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class LongCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ ArgParser get argParser => _argParser;
+ final _argParser = ArgParser(usageLineLength: 40);
+
+ @override
+ final name = 'long';
+
+ @override
+ final description =
+ 'This command has a long description that needs to be wrapped '
+ 'sometimes.\nIt has embedded newlines,\n'
+ ' and indented lines that also need to be wrapped and have their '
+ 'indentation preserved.\n${'0123456789' * 10}';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class MultilineSummaryCommand extends MultilineCommand {
+ @override
+ String get summary => description;
+}
+
+class HiddenCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'hidden';
+
+ @override
+ final description = 'Set a value.';
+
+ @override
+ final hidden = true;
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class HiddenCategorizedCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'hiddencategorized';
+
+ @override
+ final description = 'Set a value.';
+
+ @override
+ final category = 'Some category';
+
+ @override
+ final hidden = true;
+
+ @override
+ final takesArguments = false;
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class AliasedCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'aliased';
+
+ @override
+ final description = 'Set a value.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ final aliases = const ['alias', 'als', 'renamed'];
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class SuggestionAliasedCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'aliased';
+
+ @override
+ final description = 'Set a value.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ final suggestionAliases = const ['renamed'];
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class AsyncCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'async';
+
+ @override
+ final description = 'Set a value asynchronously.';
+
+ @override
+ final takesArguments = false;
+
+ @override
+ Future run() => Future<void>.value().then((_) => hasRun = true);
+}
+
+class AllowAnythingCommand extends Command {
+ bool hasRun = false;
+
+ @override
+ final name = 'allowAnything';
+
+ @override
+ final description = 'A command using allowAnything.';
+
+ @override
+ final argParser = ArgParser.allowAnything();
+
+ @override
+ void run() {
+ hasRun = true;
+ }
+}
+
+class CustomNameCommand extends Command {
+ @override
+ final String name;
+
+ CustomNameCommand(this.name);
+
+ @override
+ String get description => 'A command with a custom name';
+}
+
+void throwsIllegalArg(void Function() function, {String? reason}) {
+ expect(function, throwsArgumentError, reason: reason);
+}
+
+void throwsFormat(ArgParser parser, List<String> args, {String? reason}) {
+ expect(() => parser.parse(args), throwsA(isA<FormatException>()),
+ reason: reason);
+}
+
+void throwsArgParserException(ArgParser parser, List<String> args,
+ String message, List<String> commands, String arg) {
+ try {
+ parser.parse(args);
+ fail('Expected an ArgParserException');
+ } on ArgParserException catch (e) {
+ expect(e.message, message);
+ expect(e.commands, commands);
+ expect(e.argumentName, arg);
+ } catch (e) {
+ fail('Expected an ArgParserException, but got $e');
+ }
+}
+
+Matcher throwsUsageException(Object? message, Object? usage) =>
+ throwsA(isA<UsageException>()
+ .having((e) => e.message, 'message', message)
+ .having((e) => e.usage, 'usage', usage));
diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart
new file mode 100644
index 0000000..db502d1
--- /dev/null
+++ b/pkgs/args/test/trailing_options_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+import 'test_utils.dart';
+
+void main() {
+ test('allowTrailingOptions defaults to true', () {
+ var parser = ArgParser();
+ expect(parser.allowTrailingOptions, isTrue);
+ });
+
+ group('when trailing options are allowed', () {
+ late ArgParser parser;
+ setUp(() {
+ parser = ArgParser(allowTrailingOptions: true);
+ });
+
+ void expectThrows(List<String> args, String arg) {
+ throwsFormat(parser, args, reason: 'with allowTrailingOptions: true');
+ }
+
+ test('collects non-options in rest', () {
+ parser.addFlag('flag');
+ parser.addOption('opt', abbr: 'o');
+ var results = parser.parse(['a', '--flag', 'b', '-o', 'value', 'c']);
+ expect(results['flag'], isTrue);
+ expect(results['opt'], equals('value'));
+ expect(results.rest, equals(['a', 'b', 'c']));
+ });
+
+ test('stops parsing options at "--"', () {
+ parser.addFlag('flag');
+ parser.addOption('opt', abbr: 'o');
+ var results = parser.parse(['a', '--flag', '--', '-ovalue', 'c']);
+ expect(results['flag'], isTrue);
+ expect(results.rest, equals(['a', '-ovalue', 'c']));
+ });
+
+ test('only consumes first "--"', () {
+ parser.addFlag('flag', abbr: 'f');
+ parser.addOption('opt', abbr: 'o');
+ var results = parser.parse(['a', '--', 'b', '--', 'c']);
+ expect(results.rest, equals(['a', 'b', '--', 'c']));
+ });
+
+ test('parses a trailing flag', () {
+ parser.addFlag('flag');
+ var results = parser.parse(['arg', '--flag']);
+
+ expect(results['flag'], isTrue);
+ expect(results.rest, equals(['arg']));
+ });
+
+ test('throws on a trailing option missing its value', () {
+ parser.addOption('opt');
+ expectThrows(['arg', '--opt'], '--opt');
+ });
+
+ test('parses a trailing option', () {
+ parser.addOption('opt');
+ var results = parser.parse(['arg', '--opt', 'v']);
+ expect(results['opt'], equals('v'));
+ expect(results.rest, equals(['arg']));
+ });
+
+ test('throws on a trailing unknown flag', () {
+ expectThrows(['arg', '--xflag'], '--xflag');
+ });
+
+ test('throws on a trailing unknown option and value', () {
+ expectThrows(['arg', '--xopt', 'v'], '--xopt');
+ });
+
+ test('throws on a command', () {
+ parser.addCommand('com');
+ expectThrows(['arg', 'com'], 'com');
+ });
+ });
+
+ test("uses the innermost command's trailing options behavior", () {
+ var parser = ArgParser(allowTrailingOptions: true);
+ parser.addFlag('flag', abbr: 'f');
+ var command =
+ parser.addCommand('cmd', ArgParser(allowTrailingOptions: false));
+ command.addFlag('verbose', abbr: 'v');
+
+ var results = parser.parse(['a', '-f', 'b']);
+ expect(results['flag'], isTrue);
+ expect(results.rest, equals(['a', 'b']));
+
+ results = parser.parse(['cmd', '-f', 'a', '-v', '--unknown']);
+ expect(results['flag'], isTrue); // Not trailing.
+ expect(results.command!['verbose'], isFalse);
+ expect(results.command!.rest, equals(['a', '-v', '--unknown']));
+ });
+}
diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart
new file mode 100644
index 0000000..1167186
--- /dev/null
+++ b/pkgs/args/test/usage_test.dart
@@ -0,0 +1,537 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/args.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('ArgParser.usage', () {
+ test('negatable flags show "no-" in title', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', help: 'The mode');
+
+ validateUsage(parser, '''
+ --[no-]mode The mode
+ ''');
+ });
+
+ test('non-negatable flags don\'t show "no-" in title', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', negatable: false, help: 'The mode');
+
+ validateUsage(parser, '''
+ --mode The mode
+ ''');
+ });
+
+ test('if there are no abbreviations, there is no column for them', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', help: 'The mode');
+
+ validateUsage(parser, '''
+ --[no-]mode The mode
+ ''');
+ });
+
+ test('options are lined up past abbreviations', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', abbr: 'm', help: 'The mode');
+ parser.addOption('long', help: 'Lacks an abbreviation');
+
+ validateUsage(parser, '''
+ -m, --[no-]mode The mode
+ --long Lacks an abbreviation
+ ''');
+ });
+
+ test('help text is lined up past the longest option', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', abbr: 'm', help: 'Lined up with below');
+ parser.addOption('a-really-long-name', help: 'Its help text');
+
+ validateUsage(parser, '''
+ -m, --[no-]mode Lined up with below
+ --a-really-long-name Its help text
+ ''');
+ });
+
+ test('leading empty lines are ignored in help text', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', help: '\n\n\n\nAfter newlines');
+
+ validateUsage(parser, '''
+ --[no-]mode After newlines
+ ''');
+ });
+
+ test('trailing empty lines are ignored in help text', () {
+ var parser = ArgParser();
+ parser.addFlag('mode', help: 'Before newlines\n\n\n\n');
+
+ validateUsage(parser, '''
+ --[no-]mode Before newlines
+ ''');
+ });
+
+ test('options are documented in the order they were added', () {
+ var parser = ArgParser();
+ parser.addFlag('zebra', help: 'First');
+ parser.addFlag('monkey', help: 'Second');
+ parser.addFlag('wombat', help: 'Third');
+
+ validateUsage(parser, '''
+ --[no-]zebra First
+ --[no-]monkey Second
+ --[no-]wombat Third
+ ''');
+ });
+
+ test('the default value for a flag is shown if on', () {
+ var parser = ArgParser();
+ parser.addFlag('affirm', help: 'Should be on', defaultsTo: true);
+ parser.addFlag('negate', help: 'Should be off', defaultsTo: false);
+ parser.addFlag('null', help: 'Should be null', defaultsTo: null);
+
+ validateUsage(parser, '''
+ --[no-]affirm Should be on
+ (defaults to on)
+ --[no-]negate Should be off
+ --[no-]null Should be null
+ ''');
+ });
+
+ test('the default value for an option with no allowed list is shown', () {
+ var parser = ArgParser();
+ parser.addOption('single',
+ help: 'Can be anything', defaultsTo: 'whatevs');
+ parser.addMultiOption('multiple',
+ help: 'Can be anything', defaultsTo: ['whatevs']);
+
+ validateUsage(parser, '''
+ --single Can be anything
+ (defaults to "whatevs")
+ --multiple Can be anything
+ (defaults to "whatevs")
+ ''');
+ });
+
+ test('multiple default values for an option with no allowed list are shown',
+ () {
+ var parser = ArgParser();
+ parser.addMultiOption('any',
+ help: 'Can be anything', defaultsTo: ['some', 'stuff']);
+
+ validateUsage(parser, '''
+ --any Can be anything
+ (defaults to "some", "stuff")
+ ''');
+ });
+
+ test('no default values are shown for a multi option with an empty default',
+ () {
+ var parser = ArgParser();
+ parser.addMultiOption('implicit', help: 'Implicit default');
+ parser
+ .addMultiOption('explicit', help: 'Explicit default', defaultsTo: []);
+
+ validateUsage(parser, '''
+ --implicit Implicit default
+ --explicit Explicit default
+ ''');
+ });
+
+ test('the value help is shown', () {
+ var parser = ArgParser();
+ parser.addOption('out',
+ abbr: 'o', help: 'Where to write file', valueHelp: 'path');
+
+ validateUsage(parser, '''
+ -o, --out=<path> Where to write file
+ ''');
+ });
+
+ test('the allowed list is shown', () {
+ var parser = ArgParser();
+ parser.addOption('suit',
+ help: 'Like in cards',
+ allowed: ['spades', 'clubs', 'hearts', 'diamonds']);
+
+ validateUsage(parser, '''
+ --suit Like in cards
+ [spades, clubs, hearts, diamonds]
+ ''');
+ });
+
+ test('the default is highlighted in the allowed list', () {
+ var parser = ArgParser();
+ parser.addOption('suit',
+ help: 'Like in cards',
+ defaultsTo: 'clubs',
+ allowed: ['spades', 'clubs', 'hearts', 'diamonds']);
+
+ validateUsage(parser, '''
+ --suit Like in cards
+ [spades, clubs (default), hearts, diamonds]
+ ''');
+ });
+
+ test('multiple defaults are highlighted in the allowed list', () {
+ var parser = ArgParser();
+ parser.addMultiOption('suit',
+ help: 'Like in cards',
+ defaultsTo: ['clubs', 'diamonds'],
+ allowed: ['spades', 'clubs', 'hearts', 'diamonds']);
+
+ validateUsage(parser, '''
+ --suit Like in cards
+ [spades, clubs (default), hearts, diamonds (default)]
+ ''');
+ });
+
+ test('the allowed help is shown', () {
+ var parser = ArgParser();
+ parser.addOption('suit', help: 'Like in cards', allowed: [
+ 'spades',
+ 'clubs',
+ 'diamonds',
+ 'hearts'
+ ], allowedHelp: {
+ 'spades': 'Swords of a soldier',
+ 'clubs': 'Weapons of war',
+ 'diamonds': 'Money for this art',
+ 'hearts': 'The shape of my heart'
+ });
+
+ validateUsage(parser, '''
+ --suit Like in cards
+
+ [clubs] Weapons of war
+ [diamonds] Money for this art
+ [hearts] The shape of my heart
+ [spades] Swords of a soldier
+ ''');
+ });
+
+ test('the default is highlighted in the allowed help', () {
+ var parser = ArgParser();
+ parser.addOption('suit',
+ help: 'Like in cards',
+ defaultsTo: 'clubs',
+ allowed: [
+ 'spades',
+ 'clubs',
+ 'diamonds',
+ 'hearts'
+ ],
+ allowedHelp: {
+ 'spades': 'Swords of a soldier',
+ 'clubs': 'Weapons of war',
+ 'diamonds': 'Money for this art',
+ 'hearts': 'The shape of my heart'
+ });
+
+ validateUsage(parser, '''
+ --suit Like in cards
+
+ [clubs] (default) Weapons of war
+ [diamonds] Money for this art
+ [hearts] The shape of my heart
+ [spades] Swords of a soldier
+ ''');
+ });
+
+ test('multiple defaults are highlighted in the allowed help', () {
+ var parser = ArgParser();
+ parser.addMultiOption('suit', help: 'Like in cards', defaultsTo: [
+ 'clubs',
+ 'hearts'
+ ], allowed: [
+ 'spades',
+ 'clubs',
+ 'diamonds',
+ 'hearts'
+ ], allowedHelp: {
+ 'spades': 'Swords of a soldier',
+ 'clubs': 'Weapons of war',
+ 'diamonds': 'Money for this art',
+ 'hearts': 'The shape of my heart'
+ });
+
+ validateUsage(parser, '''
+ --suit Like in cards
+
+ [clubs] (default) Weapons of war
+ [diamonds] Money for this art
+ [hearts] (default) The shape of my heart
+ [spades] Swords of a soldier
+ ''');
+ });
+
+ test("hidden options don't appear in the help", () {
+ var parser = ArgParser();
+ parser.addOption('first', help: 'The first option');
+ parser.addOption('second', hide: true);
+ parser.addOption('third', help: 'The third option');
+
+ validateUsage(parser, '''
+ --first The first option
+ --third The third option
+ ''');
+ });
+
+ test("hidden flags don't appear in the help", () {
+ var parser = ArgParser();
+ parser.addFlag('first', help: 'The first flag');
+ parser.addFlag('second', hide: true);
+ parser.addFlag('third', help: 'The third flag');
+
+ validateUsage(parser, '''
+ --[no-]first The first flag
+ --[no-]third The third flag
+ ''');
+ });
+
+ test("hidden options don't affect spacing", () {
+ var parser = ArgParser();
+ parser.addFlag('first', help: 'The first flag');
+ parser.addFlag('second-very-long-option', hide: true);
+ parser.addFlag('third', help: 'The third flag');
+
+ validateUsage(parser, '''
+ --[no-]first The first flag
+ --[no-]third The third flag
+ ''');
+ });
+
+ test('help strings are not wrapped if usageLineLength is null', () {
+ var parser = ArgParser(usageLineLength: null);
+ parser.addFlag('long',
+ help: 'The flag with a really long help text that will not '
+ 'be wrapped.');
+ validateUsage(parser, '''
+ --[no-]long The flag with a really long help text that will not be wrapped.
+ ''');
+ });
+
+ test('help strings are wrapped properly when usageLineLength is specified',
+ () {
+ var parser = ArgParser(usageLineLength: 60);
+ parser.addFlag('long',
+ help: 'The flag with a really long help text that will be wrapped.');
+ parser.addFlag('longNewline',
+ help: 'The flag with a really long help text and newlines\n\nthat '
+ 'will still be wrapped because it is really long.');
+ parser.addFlag(
+ 'solid',
+ help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a'
+ '-word.',
+ );
+ parser.addFlag('longWhitespace',
+ help:
+ ' The flag with a really long help text and whitespace '
+ 'at the start.');
+ parser.addFlag('longTrailspace',
+ help:
+ 'The flag with a really long help text and whitespace at the end.'
+ ' ');
+ parser.addFlag('small1', help: ' a ');
+ parser.addFlag('small2', help: ' a');
+ parser.addFlag('small3', help: 'a ');
+ validateUsage(parser, '''
+ --[no-]long The flag with a really long help
+ text that will be wrapped.
+ --[no-]longNewline The flag with a really long help
+ text and newlines
+
+ that will still be wrapped because
+ it is really long.
+ --[no-]solid The-flag-with-no-whitespace-that-wi
+ ll-be-wrapped-by-splitting-a-word.
+ --[no-]longWhitespace The flag with a really long help
+ text and whitespace at the start.
+ --[no-]longTrailspace The flag with a really long help
+ text and whitespace at the end.
+ --[no-]small1 a
+ --[no-]small2 a
+ --[no-]small3 a
+ ''');
+ });
+
+ test(
+ 'help strings are wrapped with at 10 chars when usageLineLength is '
+ 'smaller than available space', () {
+ var parser = ArgParser(usageLineLength: 1);
+ parser.addFlag('long',
+ help: 'The flag with a really long help text that will be wrapped.');
+ parser.addFlag('longNewline',
+ help:
+ 'The flag with a really long help text and newlines\n\nthat will '
+ 'still be wrapped because it is really long.');
+ parser.addFlag(
+ 'solid',
+ help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-'
+ 'word.',
+ );
+ parser.addFlag('small1', help: ' a ');
+ parser.addFlag('small2', help: ' a');
+ parser.addFlag('small3', help: 'a ');
+ validateUsage(parser, '''
+ --[no-]long The flag
+ with a
+ really
+ long help
+ text that
+ will be
+ wrapped.
+ --[no-]longNewline The flag
+ with a
+ really
+ long help
+ text and
+ newlines
+
+ that will
+ still be
+ wrapped
+ because it
+ is really
+ long.
+ --[no-]solid The-flag-w
+ ith-no-whi
+ tespace-th
+ at-will-be
+ -wrapped-b
+ y-splittin
+ g-a-word.
+ --[no-]small1 a
+ --[no-]small2 a
+ --[no-]small3 a
+ ''');
+ });
+
+ test('display "mandatory" after a mandatory option', () {
+ var parser = ArgParser();
+ parser.addOption('test', mandatory: true);
+ validateUsage(parser, '''
+ --test (mandatory)
+ ''');
+ });
+
+ test('throw argument error if option is mandatory with a default value',
+ () {
+ var parser = ArgParser();
+ expect(
+ () => parser.addOption('test', mandatory: true, defaultsTo: 'test'),
+ throwsArgumentError);
+ });
+
+ group('separators', () {
+ test("separates options where it's placed", () {
+ var parser = ArgParser();
+ parser.addFlag('zebra', help: 'First');
+ parser.addSeparator('Primate:');
+ parser.addFlag('monkey', help: 'Second');
+ parser.addSeparator('Marsupial:');
+ parser.addFlag('wombat', help: 'Third');
+
+ validateUsage(parser, '''
+ --[no-]zebra First
+
+ Primate:
+ --[no-]monkey Second
+
+ Marsupial:
+ --[no-]wombat Third
+ ''');
+ });
+
+ test("doesn't add extra newlines after a multiline option", () {
+ var parser = ArgParser();
+ parser.addFlag('zebra', help: 'Multi\nline');
+ parser.addSeparator('Primate:');
+ parser.addFlag('monkey', help: 'Second');
+
+ validateUsage(parser, '''
+ --[no-]zebra Multi
+ line
+
+ Primate:
+ --[no-]monkey Second
+ ''');
+ });
+
+ test("doesn't add newlines if it's the first component", () {
+ var parser = ArgParser();
+ parser.addSeparator('Equine:');
+ parser.addFlag('zebra', help: 'First');
+
+ validateUsage(parser, '''
+ Equine:
+ --[no-]zebra First
+ ''');
+ });
+
+ test("doesn't add trailing newlines if it's the last component", () {
+ var parser = ArgParser();
+ parser.addFlag('zebra', help: 'First');
+ parser.addSeparator('Primate:');
+
+ validateUsage(parser, '''
+ --[no-]zebra First
+
+ Primate:
+ ''');
+ });
+
+ test('adds a newline after another separator', () {
+ var parser = ArgParser();
+ parser.addSeparator('First');
+ parser.addSeparator('Second');
+
+ validateUsage(parser, '''
+ First
+
+ Second
+ ''');
+ });
+ });
+ });
+}
+
+void validateUsage(ArgParser parser, String expected) {
+ expected = _unindentString(expected);
+ expect(parser.usage, equals(expected));
+}
+
+String _unindentString(String text) {
+ var lines = text.split('\n');
+
+ // Count the indentation of the last line.
+ var whitespace = RegExp('^ *');
+ var indent = whitespace.firstMatch(lines[lines.length - 1])![0]!.length;
+
+ // Drop the last line. It only exists for specifying indentation.
+ lines.removeLast();
+
+ // Strip indentation from the remaining lines.
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.length <= indent) {
+ // It's short, so it must be nothing but whitespace.
+ if (line.trim() != '') {
+ throw ArgumentError('Line "$line" does not have enough indentation.');
+ }
+
+ lines[i] = '';
+ } else {
+ if (line.substring(0, indent).trim() != '') {
+ throw ArgumentError('Line "$line" does not have enough indentation.');
+ }
+
+ lines[i] = line.substring(indent);
+ }
+ }
+
+ return lines.join('\n');
+}
diff --git a/pkgs/args/test/utils_test.dart b/pkgs/args/test/utils_test.dart
new file mode 100644
index 0000000..3cc45b8
--- /dev/null
+++ b/pkgs/args/test/utils_test.dart
@@ -0,0 +1,216 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:args/src/utils.dart';
+import 'package:test/test.dart';
+
+const _lineLength = 40;
+const _longLine = 'This is a long line that needs to be wrapped.';
+final _longLineWithNewlines =
+ 'This is a long line with newlines that\nneeds to be wrapped.\n\n'
+ '${'0123456789' * 5}';
+final _indentedLongLineWithNewlines =
+ ' This is an indented long line with newlines that\nneeds to be wrapped.'
+ '\n\tAnd preserves tabs.\n \n ${'0123456789' * 5}';
+const _shortLine = 'Short line.';
+const _indentedLongLine = ' This is an indented long line that needs to be '
+ 'wrapped and indentation preserved.';
+
+void main() {
+ group('padding', () {
+ test('can pad on the right.', () {
+ expect(padRight('foo', 6), equals('foo '));
+ });
+ });
+ group('text wrapping', () {
+ test("doesn't wrap short lines.", () {
+ expect(wrapText(_shortLine, length: _lineLength), equals(_shortLine));
+ });
+ test("doesn't wrap at all if not given a length", () {
+ expect(wrapText(_longLine), equals(_longLine));
+ });
+ test('able to wrap long lines', () {
+ expect(wrapText(_longLine, length: _lineLength), equals('''
+This is a long line that needs to be
+wrapped.'''));
+ });
+ test('wrap long lines with no whitespace', () {
+ expect(wrapText('0123456789' * 5, length: _lineLength), equals('''
+0123456789012345678901234567890123456789
+0123456789'''));
+ });
+ test('refuses to wrap to a column smaller than 10 characters', () {
+ expect(wrapText('$_longLine ${'0123456789' * 4}', length: 1), equals('''
+This is a
+long line
+that needs
+to be
+wrapped.
+0123456789
+0123456789
+0123456789
+0123456789'''));
+ });
+ test('preserves indentation', () {
+ expect(wrapText(_indentedLongLine, length: _lineLength), equals('''
+ This is an indented long line that
+ needs to be wrapped and indentation
+ preserved.'''));
+ });
+ test('preserves indentation and stripping trailing whitespace', () {
+ expect(wrapText('$_indentedLongLine ', length: _lineLength), equals('''
+ This is an indented long line that
+ needs to be wrapped and indentation
+ preserved.'''));
+ });
+ test('wraps text with newlines', () {
+ expect(wrapText(_longLineWithNewlines, length: _lineLength), equals('''
+This is a long line with newlines that
+needs to be wrapped.
+
+0123456789012345678901234567890123456789
+0123456789'''));
+ });
+ test('preserves indentation in the presence of newlines', () {
+ expect(wrapText(_indentedLongLineWithNewlines, length: _lineLength),
+ equals('''
+ This is an indented long line with
+ newlines that
+needs to be wrapped.
+\tAnd preserves tabs.
+
+ 01234567890123456789012345678901234567
+ 890123456789'''));
+ });
+ test('removes trailing whitespace when wrapping', () {
+ expect(wrapText('$_longLine \t', length: _lineLength), equals('''
+This is a long line that needs to be
+wrapped.'''));
+ });
+ test('preserves trailing whitespace when not wrapping', () {
+ expect(wrapText('$_longLine \t'), equals('$_longLine \t'));
+ });
+ test('honors hangingIndent parameter', () {
+ expect(
+ wrapText(_longLine, length: _lineLength, hangingIndent: 6), equals('''
+This is a long line that needs to be
+ wrapped.'''));
+ });
+ test('handles hangingIndent with a single unwrapped line.', () {
+ expect(wrapText(_shortLine, length: _lineLength, hangingIndent: 6),
+ equals('''
+Short line.'''));
+ });
+ test(
+ 'handles hangingIndent with two unwrapped lines and the second is empty.',
+ () {
+ expect(wrapText('$_shortLine\n', length: _lineLength, hangingIndent: 6),
+ equals('''
+Short line.
+'''));
+ },
+ );
+ test('honors hangingIndent parameter on already indented line.', () {
+ expect(wrapText(_indentedLongLine, length: _lineLength, hangingIndent: 6),
+ equals('''
+ This is an indented long line that
+ needs to be wrapped and
+ indentation preserved.'''));
+ });
+ test('honors hangingIndent parameter on already indented line.', () {
+ expect(
+ wrapText(_indentedLongLineWithNewlines,
+ length: _lineLength, hangingIndent: 6),
+ equals('''
+ This is an indented long line with
+ newlines that
+needs to be wrapped.
+ And preserves tabs.
+
+ 01234567890123456789012345678901234567
+ 890123456789'''));
+ });
+ });
+ group('text wrapping as lines', () {
+ test("doesn't wrap short lines.", () {
+ expect(wrapTextAsLines(_shortLine, length: _lineLength),
+ equals([_shortLine]));
+ });
+ test("doesn't wrap at all if not given a length", () {
+ expect(wrapTextAsLines(_longLine), equals([_longLine]));
+ });
+ test('able to wrap long lines', () {
+ expect(wrapTextAsLines(_longLine, length: _lineLength),
+ equals(['This is a long line that needs to be', 'wrapped.']));
+ });
+ test('wrap long lines with no whitespace', () {
+ expect(wrapTextAsLines('0123456789' * 5, length: _lineLength),
+ equals(['0123456789012345678901234567890123456789', '0123456789']));
+ });
+
+ test('refuses to wrap to a column smaller than 10 characters', () {
+ expect(
+ wrapTextAsLines('$_longLine ${'0123456789' * 4}', length: 1),
+ equals([
+ 'This is a',
+ 'long line',
+ 'that needs',
+ 'to be',
+ 'wrapped.',
+ '0123456789',
+ '0123456789',
+ '0123456789',
+ '0123456789'
+ ]));
+ });
+ test("doesn't preserve indentation", () {
+ expect(
+ wrapTextAsLines(_indentedLongLine, length: _lineLength),
+ equals([
+ 'This is an indented long line that needs',
+ 'to be wrapped and indentation preserved.'
+ ]));
+ });
+ test('strips trailing whitespace', () {
+ expect(
+ wrapTextAsLines('$_indentedLongLine ', length: _lineLength),
+ equals([
+ 'This is an indented long line that needs',
+ 'to be wrapped and indentation preserved.'
+ ]));
+ });
+ test('splits text with newlines properly', () {
+ expect(
+ wrapTextAsLines(_longLineWithNewlines, length: _lineLength),
+ equals([
+ 'This is a long line with newlines that',
+ 'needs to be wrapped.',
+ '',
+ '0123456789012345678901234567890123456789',
+ '0123456789'
+ ]));
+ });
+ test('does not preserves indentation in the presence of newlines', () {
+ expect(
+ wrapTextAsLines(_indentedLongLineWithNewlines, length: _lineLength),
+ equals([
+ 'This is an indented long line with',
+ 'newlines that',
+ 'needs to be wrapped.',
+ 'And preserves tabs.',
+ '',
+ '0123456789012345678901234567890123456789',
+ '0123456789'
+ ]));
+ });
+ test('removes trailing whitespace when wrapping', () {
+ expect(wrapTextAsLines('$_longLine \t', length: _lineLength),
+ equals(['This is a long line that needs to be', 'wrapped.']));
+ });
+ test('preserves trailing whitespace when not wrapping', () {
+ expect(
+ wrapTextAsLines('$_longLine \t'), equals(['$_longLine \t']));
+ });
+ });
+}