build method
Generates the outputs for a given BuildStep
.
Implementation
@override
Future<void> build(BuildStep buildStep) async {
if (!await buildStep.resolver.isLibrary(buildStep.inputId)) {
await buildStep.writeAsString(
buildStep.inputId.changeExtension(_extension),
'external void initReflector();',
);
return;
}
final library = await buildStep.inputLibrary;
final components = <ClassElement>[];
final directives = <ClassElement>[];
final injectors = <String>[];
var units = [library.definingCompilationUnit, ...library.parts];
var types = units.expand((unit) => unit.types);
var fields = units.expand((unit) => unit.topLevelVariables);
for (final clazz in types) {
final component = $Component.firstAnnotationOfExact(
clazz,
throwOnUnresolved: false,
);
if (component != null) {
components.add(clazz);
} else {
final directive = $Directive.firstAnnotationOfExact(
clazz,
throwOnUnresolved: false,
);
if (directive != null) {
directives.add(clazz);
}
}
}
for (final field in fields) {
if ($GenerateInjector.hasAnnotationOfExact(
field,
throwOnUnresolved: false,
)) {
injectors.add('${field.name}\$Injector');
}
}
// Unlike the main compiler, we do not do an allow-list check here; this is
// both to speed up the outliner (reducing duplicate checks) and because we
// do not have a configured CompileContext when the outliner is run.
final emitNullSafeCode = library.isNonNullableByDefault;
final languageVersion = emitNullSafeCode ? '' : '// @dart=2.9\n\n';
final output = StringBuffer('$languageVersion$_analyzerIgnores\n');
if (exportUserCodeFromTemplate) {
output
..writeln('// The .template.dart files also export the user code.')
..writeln("export '${p.basename(buildStep.inputId.path)}';")
..writeln();
}
if (components.isNotEmpty ||
directives.isNotEmpty ||
injectors.isNotEmpty) {
output
..writeln('// Required for referencing runtime code.')
..writeln(_angularImports);
final userLandCode = p.basename(buildStep.inputId.path);
output
..writeln('// Required for specifically referencing user code.')
..writeln("import '$userLandCode';")
..writeln();
}
output.writeln('// Required for "type inference" (scoping).');
for (final d in library.imports) {
if (!d.isDeferred && d.uri != null) {
var directive = "import '${d.uri}'";
if (d.prefix != null) {
directive += ' as ${d.prefix!.name}';
}
if (d.combinators.isNotEmpty) {
final isShow = d.combinators.first is ShowElementCombinator;
directive += isShow ? ' show ' : ' hide ';
directive += d.combinators
.map((c) {
if (c is ShowElementCombinator) {
return c.shownNames;
}
if (c is HideElementCombinator) {
return c.hiddenNames;
}
return const <Object>[];
})
.expand((i) => i)
.join(', ');
}
output.writeln('$directive;');
}
}
output.writeln();
final directiveTypeParameters = await collectTypeParameters(
components.followedBy(directives), buildStep);
if (components.isNotEmpty) {
for (final component in components) {
final componentName = component.name;
final typeArguments = _typeArgumentsFor(component);
final typeParameters = directiveTypeParameters[component.name];
final componentType = '$componentName$typeArguments';
final viewName = 'View${componentName}0';
output.write('''
// For @Component class $componentName.
external List<dynamic> get styles\$$componentName;
external _ng.ComponentFactory<$componentName> get ${componentName}NgFactory;
external _ng.ComponentFactory<$componentType> create${componentName}Factory$typeParameters();
class $viewName$typeParameters extends _ng.ComponentView<$componentType> {
external $viewName(_ng.View parentView, int parentIndex);
external String get debugComponentTypeName;
}
''');
}
}
if (directives.isNotEmpty) {
for (final directive in directives) {
final directiveName = directive.name;
final changeDetectorName = '${directiveName}NgCd';
final typeArguments = _typeArgumentsFor(directive);
final typeParameters = directiveTypeParameters[directive.name];
final directiveType = '$directiveName$typeArguments';
output.write('''
// For @Directive class $directiveName.
class $changeDetectorName$typeParameters extends _ng.DirectiveChangeDetector {
external $directiveType get instance;
external void deliverChanges();
external $changeDetectorName($directiveType instance);
external void detectHostChanges(_ng.RenderView view, _html.Element hostElement);
}
''');
}
}
if (injectors.isNotEmpty) {
for (final injector in injectors) {
output.writeln('external _ng.Injector $injector(_ng.Injector parent);');
}
}
output
..writeln()
..writeln('external void initReflector();');
await buildStep.writeAsString(
buildStep.inputId.changeExtension(_extension),
output.toString(),
);
}