blob: 6edf426f8e28bd6d74acfcdbd48d3a063b010378 [file] [log] [blame]
// Copyright (c) 2015, 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 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'constants/reader.dart';
import 'generator.dart';
import 'library.dart';
import 'output_helpers.dart';
import 'type_checker.dart';
/// Extend this type to create a [Generator] that invokes
/// [generateForAnnotatedElement] for every top level element in the source file
/// annotated with [T].
///
/// When all annotated elements have been processed, the results will be
/// combined into a single output with duplicate items collapsed.
///
/// For example, this will allow code generated for all top level elements which
/// are annotated with `@Deprecated`:
///
/// ```dart
/// class DeprecatedGenerator extends GeneratorForAnnotation<Deprecated> {
/// @override
/// Future<String> generateForAnnotatedElement(
/// Element element,
/// ConstantReader annotation,
/// BuildStep buildStep) async {
/// // Return a string representing the code to emit.
/// }
/// }
/// ```
///
/// Elements which are not at the top level, such as the members of a class or
/// extension, are not searched for annotations. To operate on, for instance,
/// annotated fields of a class ensure that the class itself is annotated with
/// [T] and use the [Element] to iterate over fields. The [TypeChecker] utility
/// may be helpful to check which elements have a given annotation.
abstract class GeneratorForAnnotation<T> extends Generator {
const GeneratorForAnnotation();
TypeChecker get typeChecker => TypeChecker.fromRuntime(T);
@override
FutureOr<String> generate(LibraryReader library, BuildStep buildStep) async {
final values = <String>{};
for (var annotatedElement in library.annotatedWith(typeChecker)) {
final generatedValue = generateForAnnotatedElement(
annotatedElement.element, annotatedElement.annotation, buildStep);
await for (var value in normalizeGeneratorOutput(generatedValue)) {
assert(value.length == value.trim().length);
values.add(value);
}
}
return values.join('\n\n');
}
/// Implement to return source code to generate for [element].
///
/// This method is invoked based on finding elements annotated with an
/// instance of [T]. The [annotation] is provided as a [ConstantReader].
///
/// Supported return values include a single [String] or multiple [String]
/// instances within an [Iterable] or [Stream]. It is also valid to return a
/// [Future] of [String], [Iterable], or [Stream].
///
/// Implementations should return `null` when no content is generated. Empty
/// or whitespace-only [String] instances are also ignored.
dynamic generateForAnnotatedElement(
Element element, ConstantReader annotation, BuildStep buildStep);
}