Skip to content

Commit

Permalink
feat: Adds support for annotations (mobxjs#904)
Browse files Browse the repository at this point in the history
* fix: Example FormStore importing non-existent file

Signed-off-by: Hugo Branco <[email protected]>

* feat: Inherited annotations for actions, observables futures and observable stream

Closes mobxjs#899

Signed-off-by: Hugo Branco <[email protected]>

* ci: Replaces implicit-casts for strict-casts

implicit-casts was deprecated in dart's 2.16

Signed-off-by: Hugo Branco <[email protected]>

---------

Signed-off-by: Hugo Branco <[email protected]>
  • Loading branch information
hugobrancowb authored Apr 14, 2023
1 parent d99a5c6 commit b3c9ad2
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 94 deletions.
4 changes: 2 additions & 2 deletions mobx/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ analyzer:
exclude:
- test/*.mocks.dart
- example/lib/*.dart
strong-mode:
implicit-casts: false
language:
strict-casts: true
3 changes: 1 addition & 2 deletions mobx/lib/version.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!!

/// The current version as per `pubspec.yaml`.
const version = '2.1.4';
const version = '2.1.4';
4 changes: 4 additions & 0 deletions mobx_codegen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.2.0

- Adds support for annotations `@protected`, `@visibleForTesting` and `@visibleForOverriding` for actions, observables futures and observables stream.

## 2.1.0 - 2.1.1

- Update analyzer version to `>=4.4.0 <6.0.0`
Expand Down
34 changes: 23 additions & 11 deletions mobx_codegen/lib/src/store_class_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,23 @@ class StoreClassVisitor extends SimpleElementVisitor {

if (element.isAsynchronous) {
final template = AsyncActionTemplate(
storeTemplate: _storeTemplate,
isObservable: _observableChecker.hasAnnotationOfExact(element),
method:
MethodOverrideTemplate.fromElement(element, typeNameFinder));
storeTemplate: _storeTemplate,
isObservable: _observableChecker.hasAnnotationOfExact(element),
method: MethodOverrideTemplate.fromElement(element, typeNameFinder),
hasProtected: element.hasProtected,
hasVisibleForOverriding: element.hasVisibleForOverriding,
hasVisibleForTesting: element.hasVisibleForTesting,
);

_storeTemplate.asyncActions.add(template);
} else {
final template = ActionTemplate(
storeTemplate: _storeTemplate,
method:
MethodOverrideTemplate.fromElement(element, typeNameFinder));
storeTemplate: _storeTemplate,
method: MethodOverrideTemplate.fromElement(element, typeNameFinder),
hasProtected: element.hasProtected,
hasVisibleForOverriding: element.hasVisibleForOverriding,
hasVisibleForTesting: element.hasVisibleForTesting,
);

_storeTemplate.actions.add(template);
}
Expand All @@ -189,14 +195,20 @@ class StoreClassVisitor extends SimpleElementVisitor {

if (_asyncChecker.returnsFuture(element)) {
final template = ObservableFutureTemplate(
method:
MethodOverrideTemplate.fromElement(element, typeNameFinder));
method: MethodOverrideTemplate.fromElement(element, typeNameFinder),
hasProtected: element.hasProtected,
hasVisibleForOverriding: element.hasVisibleForOverriding,
hasVisibleForTesting: element.hasVisibleForTesting,
);

_storeTemplate.observableFutures.add(template);
} else if (_asyncChecker.returnsStream(element)) {
final template = ObservableStreamTemplate(
method:
MethodOverrideTemplate.fromElement(element, typeNameFinder));
method: MethodOverrideTemplate.fromElement(element, typeNameFinder),
hasProtected: element.hasProtected,
hasVisibleForOverriding: element.hasVisibleForOverriding,
hasVisibleForTesting: element.hasVisibleForTesting,
);

_storeTemplate.observableStreams.add(template);
}
Expand Down
18 changes: 14 additions & 4 deletions mobx_codegen/lib/src/template/action.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import 'package:mobx_codegen/src/template/annotations_generator_mixin.dart';
import 'package:mobx_codegen/src/template/method_override.dart';
import 'package:mobx_codegen/src/template/store.dart';

class ActionTemplate {
ActionTemplate({required this.storeTemplate, required this.method});
class ActionTemplate with AnnotationsGenerator {
ActionTemplate({
required this.storeTemplate,
required this.method,
required bool hasProtected,
required bool hasVisibleForOverriding,
required bool hasVisibleForTesting,
}) {
this.hasProtected = hasProtected;
this.hasVisibleForOverriding = hasVisibleForOverriding;
this.hasVisibleForTesting = hasVisibleForTesting;
}

final StoreTemplate storeTemplate;
final MethodOverrideTemplate method;

@override
// ignore: prefer_single_quotes
String toString() => """
@override
$annotations
${method.returnType} ${method.name}${method.typeParams}(${method.params}) {
final _\$actionInfo = ${storeTemplate.actionControllerName}.startAction(name: '${storeTemplate.parentTypeName}.${method.name}${method.typeParams}');
try {
Expand Down
23 changes: 23 additions & 0 deletions mobx_codegen/lib/src/template/annotations_generator_mixin.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mixin AnnotationsGenerator {
bool hasProtected = false;
bool hasVisibleForOverriding = false;
bool hasVisibleForTesting = false;

String get annotations {
final List<String> annotations = ['@override'];

if (hasProtected) {
annotations.add('@protected');
}

if (hasVisibleForOverriding) {
annotations.add('@visibleForOverriding');
}

if (hasVisibleForTesting) {
annotations.add('@visibleForTesting');
}

return annotations.join('\n');
}
}
21 changes: 15 additions & 6 deletions mobx_codegen/lib/src/template/async_action.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import 'package:mobx_codegen/src/template/annotations_generator_mixin.dart';
import 'package:mobx_codegen/src/template/method_override.dart';
import 'package:mobx_codegen/src/template/store.dart';

class AsyncActionTemplate {
AsyncActionTemplate(
{required this.storeTemplate,
required this.isObservable,
required this.method});
class AsyncActionTemplate with AnnotationsGenerator {
AsyncActionTemplate({
required this.storeTemplate,
required this.isObservable,
required this.method,
required bool hasProtected,
required bool hasVisibleForOverriding,
required bool hasVisibleForTesting,
}) {
this.hasProtected = hasProtected;
this.hasVisibleForOverriding = hasVisibleForOverriding;
this.hasVisibleForTesting = hasVisibleForTesting;
}

final StoreTemplate storeTemplate;
final bool isObservable;
Expand All @@ -27,7 +36,7 @@ class AsyncActionTemplate {
String toString() => """
late final $_actionField = AsyncAction('${storeTemplate.parentTypeName}.${method.name}', context: context);
@override
$annotations
$_futureType${method.returnTypeArgs} ${method.name}${method.typeParams}(${method.params}) {
return $_wrappedMethodCall;
}""";
Expand Down
17 changes: 13 additions & 4 deletions mobx_codegen/lib/src/template/observable_future.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import 'package:mobx_codegen/src/template/annotations_generator_mixin.dart';
import 'package:mobx_codegen/src/template/method_override.dart';

class ObservableFutureTemplate {
ObservableFutureTemplate({required this.method});
class ObservableFutureTemplate with AnnotationsGenerator {
ObservableFutureTemplate({
required this.method,
required bool hasProtected,
required bool hasVisibleForOverriding,
required bool hasVisibleForTesting,
}) {
this.hasProtected = hasProtected;
this.hasVisibleForOverriding = hasVisibleForOverriding;
this.hasVisibleForTesting = hasVisibleForTesting;
}

final MethodOverrideTemplate method;

@override
// ignore: prefer_single_quotes
String toString() => """
@override
$annotations
ObservableFuture${method.returnTypeArgs} ${method.name}${method.typeParams}(${method.params}) {
final _\$future = super.${method.name}${method.typeArgs}(${method.args});
return ObservableFuture${method.returnTypeArgs}(_\$future, context: context);
Expand Down
17 changes: 13 additions & 4 deletions mobx_codegen/lib/src/template/observable_stream.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import 'package:mobx_codegen/src/template/annotations_generator_mixin.dart';
import 'package:mobx_codegen/src/template/method_override.dart';

class ObservableStreamTemplate {
ObservableStreamTemplate({required this.method});
class ObservableStreamTemplate with AnnotationsGenerator {
ObservableStreamTemplate({
required this.method,
required bool hasProtected,
required bool hasVisibleForOverriding,
required bool hasVisibleForTesting,
}) {
this.hasProtected = hasProtected;
this.hasVisibleForOverriding = hasVisibleForOverriding;
this.hasVisibleForTesting = hasVisibleForTesting;
}

final MethodOverrideTemplate method;

@override
// ignore: prefer_single_quotes
String toString() => """
@override
$annotations
ObservableStream${method.returnTypeArgs} ${method.name}${method.typeParams}(${method.params}) {
final _\$stream = super.${method.name}${method.typeArgs}(${method.args});
return ObservableStream${method.returnTypeArgs}(_\$stream, context: context);
Expand Down
2 changes: 1 addition & 1 deletion mobx_codegen/lib/version.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!!

/// The current version as per `pubspec.yaml`.
const version = '2.1.1';
const version = '2.2.0';
2 changes: 1 addition & 1 deletion mobx_codegen/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: mobx_codegen
description: Code generator for MobX that adds support for annotating your code with @observable, @computed, @action and also creating Store classes.
version: 2.1.1
version: 2.2.0

homepage: https://github.com/mobxjs/mobx.dart
issue_tracker: https://github.com/mobxjs/mobx.dart/issues
Expand Down
49 changes: 49 additions & 0 deletions mobx_codegen/test/data/annotations_test_class_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
library generator_sample;

import 'package:meta/meta.dart';
import 'package:mobx/mobx.dart';

part 'generator_sample.g.dart';

class AnnotationsTestClass = AnnotationsTestClassBase
with _$AnnotationsTestClass;

abstract class AnnotationsTestClassBase with Store {
AnnotationsTestClassBase(this.foo);

@observable
String foo = '';

@action
@protected
@visibleForOverriding
@visibleForTesting
void actionAnnotated() {
foo = 'Action annotated';
}

@action
@protected
@visibleForOverriding
@visibleForTesting
Future<void> asyncActionAnnotated() async {
foo = 'AsyncAction annotated';
}

@action
@observable
@protected
@visibleForOverriding
@visibleForTesting
Future<void> observableFutureAnnotated() async {
foo = 'ObservableFuture annotated';
}

@observable
@protected
@visibleForOverriding
@visibleForTesting
Stream<String> observableStreamAnnotated() async* {
yield 'ObservableFuture annotated';
}
}
76 changes: 76 additions & 0 deletions mobx_codegen/test/data/annotations_test_class_output.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
mixin _$AnnotationsTestClass on AnnotationsTestClassBase, Store {
late final _$fooAtom =
Atom(name: 'AnnotationsTestClassBase.foo', context: context);

@override
String get foo {
_$fooAtom.reportRead();
return super.foo;
}

@override
set foo(String value) {
_$fooAtom.reportWrite(value, super.foo, () {
super.foo = value;
});
}

@override
@protected
@visibleForOverriding
@visibleForTesting
ObservableStream<String> observableStreamAnnotated() {
final _$stream = super.observableStreamAnnotated();
return ObservableStream<String>(_$stream, context: context);
}

late final _$asyncActionAnnotatedAsyncAction = AsyncAction(
'AnnotationsTestClassBase.asyncActionAnnotated',
context: context);

@override
@protected
@visibleForOverriding
@visibleForTesting
Future<void> asyncActionAnnotated() {
return _$asyncActionAnnotatedAsyncAction
.run(() => super.asyncActionAnnotated());
}

late final _$observableFutureAnnotatedAsyncAction = AsyncAction(
'AnnotationsTestClassBase.observableFutureAnnotated',
context: context);

@override
@protected
@visibleForOverriding
@visibleForTesting
ObservableFuture<void> observableFutureAnnotated() {
return ObservableFuture<void>(_$observableFutureAnnotatedAsyncAction
.run(() => super.observableFutureAnnotated()));
}

late final _$AnnotationsTestClassBaseActionController =
ActionController(name: 'AnnotationsTestClassBase', context: context);

@override
@protected
@visibleForOverriding
@visibleForTesting
void actionAnnotated() {
final _$actionInfo = _$AnnotationsTestClassBaseActionController.startAction(
name: 'AnnotationsTestClassBase.actionAnnotated');
try {
return super.actionAnnotated();
} finally {
_$AnnotationsTestClassBaseActionController.endAction(_$actionInfo);
}
}

@override
String toString() {
return '''
foo: ${foo}
''';
}
}
2 changes: 1 addition & 1 deletion mobx_codegen/test/generator_usage_test.g.dart

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

7 changes: 7 additions & 0 deletions mobx_codegen/test/mobx_codegen_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ void main() {
'./data/valid_output_annotation_store_config_has_to_string.dart');
});

test(
'generates for a class containing annotations "@protected", "@visibleForTesting" and "visibleForOverriding"',
() async {
await compareFiles('./data/annotations_test_class_input.dart',
'./data/annotations_test_class_output.dart');
});

createTests([
const TestInfo(
description: 'invalid output is handled',
Expand Down
2 changes: 1 addition & 1 deletion mobx_codegen/test/nested_store.g.dart

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

Loading

0 comments on commit b3c9ad2

Please sign in to comment.