对于只运行一次并在包中创建单个文件的构建器问题的简短回答是使用r'$lib$' 作为输入扩展名。长答案是,要找到带注释的类,您可能需要一个中间输出来跟踪它们。
我将使用 2 个构建器编写此代码,一个用于搜索 ExportModel 注释,另一个用于编写导出文件。这是一个粗略的草图,省略了细节 - 我没有测试这里的任何代码,但它应该能让你走上正确的道路。
Builder 1 - 找到带有 @ExportModel() 注释的类。
可以使用package:source_gen 的一些实用程序编写,但不能使用LibraryBuilder,因为它不输出 Dart 代码...
目标是在每个.dart 文件旁边写一个.exports 文件,作为所有带有@ExportModel() 注释的类的名称。
class ExportLocatingBuilder implements Builder {
@override
final buildExtensions = const {
'.dart': ['.exports']
};
@override
Future<void> build(BuildStep buildStep) async {
final resolver = buildStep.resolver;
if (!await resolver.isLibrary(buildStep.inputId)) return;
final lib = LibraryReader(await buildStep.inputLibrary);
final exportAnnotation = TypeChecker.fromRuntime(ExportModel);
final annotated = [
for (var member in lib.annotatedWith(exportAnnotation)) element.name,
];
if (annotated.isNotEmpty) {
buildStep.writeAsString(
buildStep.inputId.changeExtension('.exports'), annotated.join(','));
}
}
}
这个构建器应该是build_to: cache,你可能想要一个PostProcessBuilder来清理它产生的所有输出,这些输出将用applies_builder指定。您可以使用FileDeletingBuilder 来廉价地实施清理。例如,请参阅 the FAQ 的临时输出和 angular cleanup。
Builder 2 - 找到 .exports 文件并生成 Dart 文件
使用findAssets 跟踪所有.exports 文件,并为每个文件编写export 语句。在文件内容中使用show,其中应包含已注释成员的名称。
class ExportsBuilder implements Builder {
@override
final buildExtensions = const {
r'$lib$': ['exports.dart']
};
@override
Future<void> build(BuildStep buildStep) async {
final exports = buildStep.findAssets(Glob('**/*.exports'));
final content = [
await for (var exportLibrary in exports)
'export \'${exportLibrary.changeExtension('.dart').uri}\' '
'show ${await buildStep.readAsString(exportLibrary)};',
];
if (content.isNotEmpty) {
buildStep.writeAsString(
AssetId(buildStep.inputId.package, 'lib/exports.dart'),
content.join('\n'));
}
}
}
如果你想在 pub 上发布这个文件,这个构建器应该是 build_to: source。它应该有一个required_inputs: [".exports"] 以确保它在前一个构建器之后运行。
为什么需要这么复杂?
您可以将其实现为使用findAssets 查找所有 Dart 文件的单个构建器。缺点是重建会慢得多,因为它会因任何 Dart 文件中的任何内容更改而失效,并且您最终会解析 all Dart 代码以更改 any 飞镖代码。使用 2 builder 方法,只有来自更改的 Dart 文件的单个 .exports 需要在更改时解析和重建,然后只有在导出更改时,exports.dart 文件才会失效。
旧版本的build_runner 也不支持使用Resolver 来解析不是从输入库中传递导入的代码。最新版本的build_runner 放宽了这一限制。