Codegenerierung in Dart. Teil 2. Anmerkungen, source_gen und build_runner

Im ersten Teil haben wir herausgefunden, warum Codegenerierung erforderlich ist, und die erforderlichen Tools für die Codegenerierung in Dart aufgelistet. Im zweiten Teil erfahren Sie, wie Sie Anmerkungen in Dart erstellen und verwenden sowie wie Sie source_gen und build_runner verwenden , um die Codegenerierung zu starten.



Dart-Anmerkungen


Anmerkungen sind syntaktische Metadaten, die dem Code hinzugefügt werden können. Mit anderen Worten, es besteht die Möglichkeit, jeder Komponente des Codes zusätzliche Informationen hinzuzufügen, z. B. einer Klasse oder Methode. Anmerkungen werden im Dart-Code häufig verwendet: Wir verwenden @required um anzuzeigen, dass ein benannter Parameter erforderlich ist, und unser Code wird nicht kompiliert, wenn der mit Anmerkungen versehene Parameter nicht angegeben wird. Wir verwenden @override auch, um @override , dass eine bestimmte in einer übergeordneten Klasse definierte API in einer @override Klasse implementiert ist. Anmerkungen beginnen immer mit dem @ -Symbol.


Wie erstelle ich eine eigene Anmerkung?


Obwohl die Idee, dem Code Metadaten hinzuzufügen, etwas exotisch und kompliziert klingt, sind Anmerkungen eines der einfachsten Dinge in der Dart-Sprache. Es wurde bereits gesagt, dass Anmerkungen lediglich zusätzliche Informationen enthalten . Sie ähneln PODO (Plain Old Dart Objects). Und jede Klasse kann als Annotation dienen, wenn darin ein const Konstruktor definiert ist :


 class { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } @Todo('hello first annotation', todoUrl: 'https://www.google.com') class HelloAnnotations {} 

Wie Sie sehen können, sind Anmerkungen sehr einfach. Und was zählt, ist, was wir mit diesen Anmerkungen machen werden. Dies wird uns helfen, source_gen und build_runner .


Wie benutzt man build_runner?


build_runner ist ein Dart-Paket, mit dem wir Dateien mit Dart-Code generieren können. Wir werden Builder Dateien mit build.yaml . Wenn es konfiguriert ist, wird Builder bei jedem build Befehl aufgerufen oder wenn die Datei geändert wird. Wir haben auch die Möglichkeit, Code zu analysieren, der geändert wurde oder bestimmte Kriterien erfüllt.


source_gen zum Verständnis von Dartcode


In gewisser build_runner ist build_runner ein Mechanismus, der die Frage " Wann muss ich Code generieren?" Beantwortet. Gleichzeitig beantwortet source_gen die Frage „ Welcher Code soll generiert werden?“. source_gen bietet ein Framework zum Erstellen von Buildern, damit build_runner funktioniert. Außerdem bietet source_gen eine praktische API zum Parsen und Generieren von Code.


Alles zusammen: TODO-Bericht


Im Rest dieses Artikels werden wir das Projekt todo_reporter.dart zerlegen , das hier zu finden ist .


Es gibt eine ungeschriebene Regel, der alle Projekte folgen, die die Codegenerierung verwenden: Sie müssen ein Paket mit Anmerkungen und ein separates Paket für den Generator erstellen, der diese Anmerkungen verwendet. Informationen zum Erstellen einer Paketbibliothek in Dart / Flutter finden Sie hier .


Zuerst müssen Sie das Verzeichnis todo_reporter.dart erstellen. In diesem Verzeichnis müssen Sie das Verzeichnis todo_reporter erstellen, das die Anmerkung enthält, das Verzeichnis todo_reporter_generator , um die Anmerkung zu verarbeiten, und schließlich das example das eine Demonstration der Funktionen der zu erstellenden Bibliothek enthält.


Das Suffix .dart aus Gründen der Übersichtlichkeit zum Stammverzeichnisnamen hinzugefügt. Dies ist natürlich nicht erforderlich, aber ich befolge diese Regel gerne, um genau anzuzeigen, dass dieses Paket in jedem Dart-Projekt verwendet werden kann. Im Gegenteil, wenn ich angeben wollte, dass dieses Paket nur für Flutter ist (wie ozzie.flutter ), würde ich ein anderes Suffix verwenden. Dies ist nicht notwendig, es ist nur eine Namenskonvention, die ich einzuhalten versuche.


Erstellen Sie todo_reporter, unser einfaches Anmerkungspaket


Wir werden todo_reporter in todo_reporter.dart erstellen. Erstellen Sie dazu die Datei pubspec.yaml und das Verzeichnis lib .


pubspec.yaml sehr einfach:


 name: todo_reporter description: Keep track of all your TODOs. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: dev_dependencies: test: 1.3.4 

Es gibt keine Abhängigkeiten außer dem im Entwicklungsprozess verwendeten Testpaket.


lib im lib Verzeichnis wie folgt vor:


  • Sie müssen die Datei todo_reporter.dart erstellen, in der beim export alle Klassen mit einer öffentlichen API angegeben werden. Dies ist eine gute Vorgehensweise, da jede Klasse in unserem Paket mit import 'package:todo_reporter/todo_reporter.dart'; importiert werden kann import 'package:todo_reporter/todo_reporter.dart'; . Sie können diese Klasse hier sehen .
  • Innerhalb des lib Verzeichnisses erstellen wir das src Verzeichnis, das den gesamten Code enthält - öffentlich und nicht öffentlich.

In unserem Fall müssen wir lediglich eine Anmerkung hinzufügen. Erstellen wir eine todo.dart Datei mit unserer Anmerkung:


 class Todo { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } 

Das ist alles, was Sie zum Kommentieren benötigen. Ich sagte, dass es einfach sein wird. Das ist aber noch nicht alles. Fügen wir dem Testverzeichnis Unit-Tests hinzu:


todo_test.dart
 import 'package:test/test.dart'; import 'package:todo_reporter/todo_reporter.dart'; void main() { group('Todo annotation', () { test('must have a non-null name', () { expect(() => Todo(null), throwsA(TypeMatcher<AssertionError>())); }); test('does not need to have a todoUrl', () { final todo = Todo('name'); expect(todo.todoUrl, null); }); test('if it is a given a todoUrl, it will be part of the model', () { final givenUrl = 'http://url.com'; final todo = Todo('name', todoUrl: givenUrl); expect(todo.todoUrl, givenUrl); }); }); } 

Dies ist alles, was wir zum Erstellen der Anmerkung benötigen. Den Code finden Sie hier . Jetzt können wir zum Generator gehen.


Einen coolen Job machen: todo_reporter_generator


todo_reporter_generator wir nun wissen, wie Pakete erstellt werden, erstellen todo_reporter_generator Paket todo_reporter_generator . In diesem Paket sollten sich die build.yaml pubspec.yaml und build.yaml sowie ein lib Verzeichnis befinden. Das lib Verzeichnis muss das src Verzeichnis und die Datei builder.dart enthalten. Unser todo_reporter_generator wird als separates Paket betrachtet, das als dev_dependency zu anderen Projekten hinzugefügt wird. Dies liegt daran, dass die Codegenerierung nur in der Entwicklungsphase erforderlich ist und nicht zur fertigen Anwendung hinzugefügt werden muss.


pubspec.yaml wie folgt:


 name: todo_reporter_generator description: An annotation processor for @Todo annotations. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: build: '>=0.12.0 <2.0.0' source_gen: ^0.9.0 todo_reporter: path: ../todo_reporter/ dev_dependencies: build_test: ^0.10.0 build_runner: '>=0.9.0 <0.11.0' test: ^1.0.0 

Jetzt erstellen build.yaml . Diese Datei enthält die für unsere Builder erforderliche Konfiguration. Weitere Details finden Sie hier . build.yaml wie folgt:


 targets: $default: builders: todo_reporter_generator|todo_reporter: enabled: true builders: todo_reporter: target: ":todo_reporter_generator" import: "package:todo_reporter_generator/builder.dart" builder_factories: ["todoReporter"] build_extensions: {".dart": [".todo_reporter.g.part"]} auto_apply: dependents build_to: cache applies_builders: ["source_gen|combining_builder"] 

Die import Eigenschaft verweist auf die Datei, die den Builder enthält, und die Eigenschaft builder_factories verweist auf die Methoden, mit denen der Code generiert wird.


Jetzt können wir die Datei builder.dart im lib Verzeichnis erstellen:


 import 'package:build/build.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter_generator/src/todo_reporter_generator.dart'; Builder todoReporter(BuilderOptions options) => SharedPartBuilder([TodoReporterGenerator()], 'todo_reporter'); 

Und die Datei todo_reporter_generator.dart im Verzeichnis src :


 import 'dart:async'; import 'package:analyzer/dart/element/element.dart'; import 'package:build/src/builder/build_step.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter/todo_reporter.dart'; class TodoReporterGenerator extends GeneratorForAnnotation<Todo> { @override FutureOr<String> generateForAnnotatedElement( Element element, ConstantReader annotation, BuildStep buildStep) { return "// Hey! Annotation found!"; } } 

Wie Sie sehen können, builder.dart wir in der Datei todoReporter die vom Builder erstellte Methode builder.dart definiert. Builder wird mit dem SharedPartBuilder , der unseren TodoReporterGenerator . build_runner und source_gen arbeiten also zusammen.


Unser TodoReporterGenerator ist eine Unterklasse von GeneratorForAnnotation , daher wird die generateForAnnotatedElement Methode nur ausgeführt, wenn diese Annotation (in unserem Fall @Todo ) im Code gefunden wird.


Die generateForAnnotatedElement Methode gibt eine Zeichenfolge zurück, die unseren generierten Code enthält. Wenn der generierte Code nicht kompiliert wird, schlägt die gesamte Erstellungsphase fehl . Dies ist sehr nützlich, da es Fehler in der Zukunft vermeidet.


Daher erstellt unser todo_repoter_generator bei jeder Codegenerierung eine todo_repoter_generator mit dem Kommentar // Hey! Annotation found! // Hey! Annotation found! Im nächsten Artikel erfahren Sie, wie Sie Anmerkungen verarbeiten.


Alles zusammen: mit todo_reporter


Jetzt können Sie zeigen, wie todo_reporter.dart . Es wird empfohlen, ein example hinzuzufügen, wenn Sie mit Paketen arbeiten. So können andere Entwickler sehen, wie die API in einem realen Projekt verwendet werden kann.


Lassen Sie uns ein Projekt erstellen und die erforderlichen Abhängigkeiten zu pubspec.yaml . In unserem Fall erstellen wir ein Flutter-Projekt im example und fügen die Abhängigkeiten hinzu:


 dependencies: flutter: sdk: flutter todo_reporter: path: ../todo_reporter/ dev_dependencies: build_runner: 1.0.0 flutter_test: sdk: flutter todo_reporter_generator: path: ../todo_reporter_generator/ 

Nach Erhalt der Pakete ( flutter packages get ) können wir unsere Anmerkung verwenden:


 import 'package:todo_reporter/todo_reporter.dart'; @Todo('Complete implementation of TestClass') class TestClass {} 

Nachdem alles vorhanden ist, lassen Sie unseren Generator laufen:


 $ flutter packages pub run build_runner build 

Nachdem der Befehl ausgeführt wurde, werden Sie eine neue Datei in unserem Projekt todo.g.dart : todo.g.dart . Es wird Folgendes enthalten:


 // GENERATED CODE - DO NOT MODIFY BY HAND part of 'todo.dart'; // ***************************************************************** // TodoReporterGenerator // ******************************************************************** // Hey! Annotation found! 

Wir haben erreicht, was wir wollten! Jetzt können wir die richtige Dart-Datei für jede @Todo Annotation in unserem Code generieren. Versuchen Sie, so viele zu erstellen, wie Sie benötigen.


Im nächsten Artikel


Jetzt haben wir die richtigen Einstellungen zum Generieren von Dateien. Im nächsten Artikel erfahren Sie, wie Sie Anmerkungen verwenden, damit der generierte Code wirklich coole Dinge bewirken kann. Schließlich macht der Code, der jetzt generiert wird, wenig Sinn.

Source: https://habr.com/ru/post/de446264/


All Articles