Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests/goldens #4

Merged
merged 13 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,15 @@ environment:
sdk: ^3.0.0

dependencies:
cli_annotations: ^0.1.0-dev.1
cli_annotations: ^0.1.0-dev.4

dev_dependencies:
build_runner: ^2.4.8
cli_gen: ^0.1.0-dev.1
build_runner: ^2.4.8
cli_gen: ^0.1.0-dev.4

# define an executable name (optional)
executables:
dart_git:
path: main # file name of `main()` in bin/ directory
dart_git: main
```

You can optionally define an executable name and activate it using [pub global activate](https://dart.dev/tools/pub/cmd/pub-global#activating-a-package-on-your-local-machine).
Expand Down Expand Up @@ -121,7 +120,7 @@ class GitRunner extends _$GitRunner {

### Define a Command

Create a `Command` by simply creating a method on the class; any type can be used as a parameter.
Inside the `CommandRunner` class, create a `Command` by creating a method and annotating it with `@cliCommand`. See the [Features](#features) section for more information on the supported types and features.

```dart
@cliRunner
Expand All @@ -137,14 +136,35 @@ class GitRunner extends _$GitRunner {
}
```

You can create as many commands inside the `CommandRunner` class as you'd like:

```dart
@cliRunner
class GitRunner extends _$GitRunner {
@cliCommand
Future<void> merge({
required String branch,
MergeStrategy strategy = MergeStrategy.ort,
bool? commit,
}) async { /* ... */ }

@cliCommand
Future<void> stashPush() async { /* ... */ }

@cliCommand
Future<void> stashPop() async { /* ... */ }

// ...
}
```

### Define a Subcommand

As your application grows, you may want to separate your commands into their own groups.

You can create a `Subcommand` by annotating a class with `@cliSubcommand` and extending the generated superclass.
To do so, create a `Subcommand` class by annotating the class with `@cliSubcommand` and extending the generated superclass.

```dart
// Create your subcommand
@cliSubcommand
class StashSubcommand extends _$StashSubcommand {
@cliCommand
Expand All @@ -154,11 +174,15 @@ class StashSubcommand extends _$StashSubcommand {
Future<void> pop() async { /* ... */ }
}

// Then mount it to your `CommandRunner` or a parent `Subcommand`
```

Subcommands can then be connected to the main `CommandRunner` class, or to another `Subcommand` class, by using the `@cliMount` annotation.

```dart
@cliRunner
class GitRunner extends _$GitRunner {
@mount
Command get stash => StashSubcommand();
@cliMount
StashSubcommand get stash => StashSubcommand();
}
```

Expand Down
4 changes: 2 additions & 2 deletions docs/pub_example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ packages:
path: "../../src/cli_annotations"
relative: true
source: path
version: "0.1.0-dev.3"
version: "0.1.0-dev.4"
cli_gen:
dependency: "direct dev"
description:
path: "../../src/cli_gen"
relative: true
source: path
version: "0.1.0-dev.3"
version: "0.1.0-dev.4"
code_builder:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion example/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ analyzer:
linter:
rules:
prefer_relative_imports: true
prefer_if_null_operators: false
# prefer_if_null_operators: false
1 change: 0 additions & 1 deletion example/lib/runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class GitRunner extends _$GitRunner<void> {
int? fooWithDefault,

/// Perform the merge and commit the result.

bool? commit,
}) async {
print('Merging branch $branch');
Expand Down
9 changes: 8 additions & 1 deletion example/lib/runner.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 4 additions & 7 deletions example/lib/stash.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ packages:
path: "../src/cli_annotations"
relative: true
source: path
version: "0.1.0-dev.2"
version: "0.1.0-dev.4"
cli_gen:
dependency: "direct dev"
description:
path: "../src/cli_gen"
relative: true
source: path
version: "0.1.0-dev.2"
version: "0.1.0-dev.4"
code_builder:
dependency: transitive
description:
Expand Down
3 changes: 2 additions & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ packages:
- src/cli_annotations
- src/cli_gen
- src/cli_gen/test/**
- test/goldens

scripts:
analyze:
Expand All @@ -23,7 +24,7 @@ scripts:
exec: dart test

packageFilters:
published: true
dirExists: test
build:
run: dart run build_runner build -d

Expand Down
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ environment:
dependencies:
analyzer: ^6.4.1
args: ^2.4.2
cli_annotations: ^0.1.0-dev.2
cli_annotations: ^0.1.0-dev.4
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1

dev_dependencies:
build_runner: ^2.4.8
cli_gen: ^0.1.0-dev.2
cli_gen: ^0.1.0-dev.4
freezed: ^2.4.7
json_serializable: ^6.7.1
lints: ^3.0.0
Expand Down
4 changes: 4 additions & 0 deletions src/cli_annotations/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.0-dev.5

- feat: Warning on `cliCommand` use on a class, rather than a method/function

## 0.1.0-dev.4

- feat: Support for adding a generic type to `Command` and `CommandRunner`
Expand Down
2 changes: 1 addition & 1 deletion src/cli_annotations/lib/src/commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import 'package:meta/meta_meta.dart';
/// }
/// ```
/// {@endtemplate}
@Target({TargetKind.method, TargetKind.function, TargetKind.classType})
@Target({TargetKind.method, TargetKind.function})
class CliCommand {
final String? name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
}

throw InvalidGenerationSourceError(
'Invalid type for constant value: $thisType',
'Unknown type for constant value: $thisType',

Check warning on line 134 in src/cli_gen/lib/src/analysis/parameters/default_value_code_builder.dart

View check run for this annotation

Codecov / codecov/patch

src/cli_gen/lib/src/analysis/parameters/default_value_code_builder.dart#L134

Added line #L134 was not covered by tests
);
}

Expand Down
20 changes: 12 additions & 8 deletions src/cli_gen/lib/src/code/command/command_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ class CommandBuilder {
}),
);

const argParserBuilder = ArgParserInstanceExp();
builder.body = argParserBuilder
.buildArgParserCascadeFromRef(model.parameters)
.statement;
if (model.parameters.isNotEmpty) {
const argParserBuilder = ArgParserInstanceExp();
builder.body = argParserBuilder
.buildArgParserCascadeFromRef(model.parameters)
.statement;
}
}),
);

Expand All @@ -52,6 +54,7 @@ class CommandBuilder {
builder.modifier = FieldModifier.final$;

builder.type = FunctionType((builder) {
builder.returnType = model.returnType;
final requiredPositionalParams =
model.parameters.where((e) => !e.isNamed && e.isRequired);
builder.requiredParameters.addAll(
Expand Down Expand Up @@ -127,10 +130,11 @@ class CommandBuilder {

builder.statements.addAll([
// -- declare a `results` variable --
declareFinal('results')
.assign(refer('argResults'))
.nullChecked
.statement,
if (model.parameters.isNotEmpty)
declareFinal('results')
.assign(refer('argResults'))
.nullChecked
.statement,

// -- call the user method --
userMethodCallBuilder.buildInlineCallStatement(model),
Expand Down
33 changes: 26 additions & 7 deletions src/cli_gen/lib/src/code/command/runner_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class RunnerBuilder {
builder.symbol = 'T';
}));

builder.docs.addAll([
if (model.docComments != null) '/// ${model.docComments}\n///',
_classDocComments,
]);

builder.constructors.add(
Constructor((builder) {
// -- super initializer --
Expand All @@ -60,21 +65,28 @@ class RunnerBuilder {
);

// -- the constructor body --
// adds nested commands and `@mount` subcommands to the CommandRunner
final bodyBuilder = SubcommandConstructorBodyBuilder();
builder.body = bodyBuilder.buildSubcommandConstructorBody(model);
if (model.commandMethods.isNotEmpty ||
model.mountedSubcommands.isNotEmpty) {
// adds nested commands and `@mount` subcommands to the CommandRunner
final bodyBuilder = SubcommandConstructorBodyBuilder();
builder.body = bodyBuilder.buildSubcommandConstructorBody(model);
}
}),
);

// modifies the return type as a nullable, then wraps it in a Future
final runReturnType = TypeReference((builder) {
builder.symbol = Identifiers.dart.future.symbol;
builder.url = Identifiers.dart.future.url;
final nestedType = (model.bound ?? Identifiers.dart.dynamic);
final nestedType =
(model.bound ?? Identifiers.dart.dynamic).toTypeRef();
// `void?` and `dynamic?` are not valid, but any other type should be
// nullable (according to `args``CommandRunner.run` return type)
final isNullable =
nestedType.symbol != 'void' && nestedType.symbol != 'dynamic';

builder.types.add(
nestedType.toTypeRef(
isNullable: nestedType.symbol == 'void' ? null : true,
),
nestedType.toTypeRef(isNullable: isNullable),
);
});

Expand Down Expand Up @@ -115,3 +127,10 @@ class RunnerBuilder {
});
}
}

const _classDocComments = '''
/// 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.''';
21 changes: 12 additions & 9 deletions src/cli_gen/lib/src/code/command/subcommand_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class SubcommandBuilder {
const commandBuilder = CommandBuilder();
final subcommandClass = buildSubcommandClass(model);

final subcommands = model.commandMethods.map((e) {
return commandBuilder.buildCommandClass(e);
});
final subcommands = model.commandMethods.map(
commandBuilder.buildCommandClass,
);

return [subcommandClass, ...subcommands];
}
Expand All @@ -48,12 +48,15 @@ class SubcommandBuilder {
// -- class constructor --
builder.constructors.add(
Constructor((constructor) {
constructor.body = Block((block) {
final constructorBodyBuilder = SubcommandConstructorBodyBuilder();
block.statements.add(
constructorBodyBuilder.buildSubcommandConstructorBody(model),
);
});
if (model.commandMethods.isNotEmpty ||
model.mountedSubcommands.isNotEmpty) {
constructor.body = Block((block) {
final constructorBodyBuilder = SubcommandConstructorBodyBuilder();
block.statements.add(
constructorBodyBuilder.buildSubcommandConstructorBody(model),
);
});
}
}),
);

Expand Down
Loading