Pembuatan Kode di Dart. Bagian 2. Anotasi, source_gen dan build_runner

Pada bagian pertama, kami menemukan mengapa pembuatan kode diperlukan dan mendaftar alat yang diperlukan untuk pembuatan kode di Dart. Pada bagian kedua, kita akan belajar cara membuat dan menggunakan anotasi di Dart, serta cara menggunakan source_gen dan build_runner untuk memulai pembuatan kode.



Penjelasan Dart


Anotasi adalah metadata sintaksis yang dapat ditambahkan ke kode. Dengan kata lain, ini adalah kesempatan untuk menambahkan informasi tambahan ke komponen kode apa pun, misalnya, ke kelas atau metode. Anotasi banyak digunakan dalam kode Dart: kami menggunakan @required untuk menunjukkan bahwa parameter bernama diperlukan, dan kode kami tidak akan dikompilasi jika parameter anotasi tidak ditentukan. Kami juga menggunakan @override untuk menunjukkan bahwa API tertentu yang ditentukan dalam kelas induk diimplementasikan dalam kelas anak. Anotasi selalu dimulai dengan simbol @ .


Bagaimana cara membuat anotasi Anda sendiri?


Meskipun gagasan menambahkan metadata ke kode terdengar agak eksotis dan rumit, penjelasan adalah salah satu hal paling sederhana dalam bahasa Dart. Sebelumnya dikatakan bahwa anotasi hanya membawa informasi tambahan . Mereka mirip dengan PODO (Plain Old Dart Objects). Dan setiap kelas dapat berfungsi sebagai anotasi jika konstruktor const didefinisikan di dalamnya :


 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 {} 

Seperti yang Anda lihat, anotasi sangat sederhana. Dan yang penting adalah apa yang akan kita lakukan dengan anotasi ini. Ini akan membantu kami source_gen dan build_runner .


Bagaimana cara menggunakan build_runner?


build_runner adalah paket Dart yang akan membantu kami menghasilkan file menggunakan kode Dart. Kami akan mengonfigurasi file Builder menggunakan build.yaml . Ketika dikonfigurasi, Builder akan dipanggil pada setiap perintah build atau ketika file diubah. Kami juga memiliki peluang untuk mem-parsing kode yang telah dimodifikasi atau memenuhi kriteria tertentu.


source_gen untuk memahami kode Dart


Dalam arti tertentu, build_runner adalah mekanisme yang menjawab pertanyaan " Kapan saya perlu membuat kode?" Pada saat yang sama, source_gen menjawab pertanyaan "Kode apa yang harus dihasilkan?". source_gen menyediakan kerangka kerja untuk membuat Builder agar build_runner berfungsi. source_gen juga menyediakan API yang mudah digunakan untuk parsing dan menghasilkan kode.


Menyatukan semuanya: Laporan TODO


Di sisa artikel ini, kami akan membongkar proyek todo_reporter.dart , yang dapat ditemukan di sini .


Ada aturan tidak tertulis yang diikuti oleh semua proyek yang menggunakan pembuatan kode: Anda perlu membuat paket yang berisi anotasi , dan paket terpisah untuk generator yang menggunakan anotasi ini. Informasi tentang cara membuat pustaka paket di Dart / Flutter dapat ditemukan di sini .


Pertama, Anda perlu membuat direktori todo_reporter.dart . Di dalam direktori ini Anda perlu membuat direktori todo_reporter , yang akan berisi anotasi, direktori todo_reporter_generator untuk memproses anotasi, dan, akhirnya, direktori example berisi demonstrasi kemampuan perpustakaan yang sedang dibuat.


Sufiks .dart ditambahkan ke nama direktori root untuk kejelasan. Tentu saja, ini tidak perlu, tetapi saya ingin mengikuti aturan ini untuk secara akurat menunjukkan fakta bahwa paket ini dapat digunakan dalam proyek Dart apa pun. Sebaliknya, jika saya ingin menunjukkan bahwa paket ini hanya untuk Flutter (seperti ozzie.flutter ), saya akan menggunakan akhiran yang berbeda. Ini tidak perlu, itu hanya konvensi penamaan yang saya coba patuhi.


Membuat todo_reporter, paket penjelasan sederhana kami


Kita akan membuat todo_reporter di dalam todo_reporter.dart . Untuk melakukan ini, buat file pubspec.yaml dan direktori lib .


pubspec.yaml sangat sederhana:


 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 

Tidak ada dependensi kecuali untuk paket test yang digunakan dalam proses pengembangan.


Di direktori lib , lakukan hal berikut:


  • Anda perlu membuat file todo_reporter.dart , di mana, menggunakan export , semua kelas yang memiliki API publik akan ditentukan. Ini adalah praktik yang baik, karena setiap kelas dalam paket kami dapat diimpor menggunakan import 'package:todo_reporter/todo_reporter.dart'; . Anda dapat melihat kelas ini di sini .
  • Di dalam direktori lib , kita akan membuat direktori src berisi semua kode - publik dan non-publik.

Dalam kasus kami, semua yang perlu kami tambahkan adalah anotasi. Mari kita buat file todo.dart dengan anotasi kami:


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

Jadi hanya itu yang diperlukan untuk membuat catatan. Saya mengatakan bahwa itu akan sederhana. Tapi itu belum semuanya. Mari menambahkan tes unit ke direktori test :


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); }); }); } 

Ini yang kita butuhkan untuk membuat anotasi. Anda dapat menemukan kode di sini . Sekarang kita bisa pergi ke generator.


Membuat pekerjaan keren: todo_reporter_generator


Sekarang kita tahu cara membuat paket, mari kita buat paket todo_reporter_generator . Di dalam paket ini harus ada build.yaml dan build.yaml dan direktori lib . Direktori lib harus memiliki direktori src dan file builder.dart . todo_reporter_generator kami dianggap sebagai paket terpisah yang akan ditambahkan sebagai dev_dependency ke proyek lain. Ini karena pembuatan kode hanya diperlukan pada tahap pengembangan, dan tidak perlu ditambahkan ke aplikasi yang sudah selesai.


pubspec.yaml sebagai berikut:


 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 

Sekarang mari kita buat build.yaml . File ini berisi konfigurasi yang diperlukan untuk Pembangun kami. Rincian lebih lanjut dapat ditemukan di sini . build.yaml sebagai berikut:


 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"] 

Properti import menunjuk ke file yang berisi Builder , dan properti builder_factories menunjuk ke metode yang akan menghasilkan kode.


Sekarang kita dapat membuat file builder.dart di direktori lib :


 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'); 

Dan file todo_reporter_generator.dart di direktori 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!"; } } 

Seperti yang Anda lihat, dalam file builder.dart kami mendefinisikan metode todoReporter yang dibuat oleh Builder . Builder dibuat menggunakan SharedPartBuilder , yang menggunakan TodoReporterGenerator kami. Jadi build_runner dan source_gen bekerja bersama.


TodoReporterGenerator kami adalah subkelas dari GeneratorForAnnotation , jadi metode generateForAnnotatedElement hanya akan dieksekusi ketika anotasi ini ( @Todo dalam kasus kami) ditemukan dalam kode.


Metode generateForAnnotatedElement mengembalikan string yang berisi kode yang kami hasilkan. Jika kode yang dihasilkan tidak dikompilasi, maka seluruh fase build akan gagal . Ini sangat berguna karena menghindari kesalahan di masa depan.


Jadi, dengan setiap pembuatan kode, todo_repoter_generator kami akan membuat file part , dengan komentar // Hey! Annotation found! // Hey! Annotation found! Pada artikel selanjutnya, kita akan belajar cara memproses anotasi.


Menyatukan semuanya: menggunakan todo_reporter


Sekarang Anda dapat mendemonstrasikan cara todo_reporter.dart . Merupakan praktik yang baik untuk menambahkan example proyek saat bekerja dengan paket. Jadi pengembang lain akan dapat melihat bagaimana API dapat digunakan dalam proyek nyata.


Mari kita membuat proyek dan menambahkan dependensi yang diperlukan ke pubspec.yaml . Dalam kasus kami, kami akan membuat proyek Flutter di dalam direktori example dan menambahkan dependensi:


 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/ 

Setelah menerima paket (paket flutter packages get ) kita dapat menggunakan anotasi kami:


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

Sekarang semuanya sudah di tempat, jalankan generator kami:


 $ flutter packages pub run build_runner build 

Setelah perintah selesai, Anda akan melihat file baru di proyek kami: todo.g.dart . Ini akan berisi yang berikut:


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

Kami mencapai apa yang kami inginkan! Sekarang kita dapat menghasilkan file Dart yang benar untuk setiap anotasi @Todo dalam kode kita. Coba dan buat sebanyak yang Anda butuhkan.


Di artikel selanjutnya


Sekarang kami memiliki pengaturan yang benar untuk menghasilkan file. Pada artikel selanjutnya, kita akan belajar cara menggunakan anotasi sehingga kode yang dihasilkan dapat melakukan hal-hal yang sangat keren. Bagaimanapun, kode yang sedang dibuat sekarang tidak masuk akal.

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


All Articles