-
Notifications
You must be signed in to change notification settings - Fork 248
The Angular Transformer
Dart's package manager pub supports a system of pluggable processing scripts called transformers. You can find more info about them at https://www.dartlang.org/tools/pub/assets-and-transformers.html
The angular.dart library comes with a transformer of its own. Its purpose is to generate static structures that remove the need for reflection (dart:mirrors
) at runtime. This guarantees your application JS size is minimal after dart2js compilation.
To use the Angular transformer include the following line in your pubspec.yaml
:
transformers:
- angular
The transformer goes through the following steps in sequential order:
- Call into
observe
transformer. - Find all dart files referenced with
<script type="application/dart">
. - Create injection information for all classes used by dependency injection.
- Transform relative urls.
- Create setters and getters for all expressions.
- Extract all metadata annotations.
- Transform your application entry-point to use
staticApplicationFactory
.
The code entry point is lib/transformer.dart
. Let's go through each step in detail.
Angular.dart's change detection supports package:observe @Observable
objects, so we need to call into their transformer first.
Dart's transformer library will go through all /lib
files, but it misses .dart
files directly referenced from files in /web
, thus necessitating this step. This transformer generates files that look like *.dart.html_reference
.
Dependency injection needs to be able to reflect on the constructor of an injectable class.
For example if I have a class Car
that depends on an Engine
and Brakes
classes, dependency injection needs:
- a mapping from
Car
to the list of required types[Engine, Brakes]
. - a closure to actually call when the requirements are found -
(anEngine, someBreaks) => new Car(anEngine, someBreaks)
.
This transformer creates these mappings for all classes annotated with @Injectable()
and stores them in <your_file>_generated_type_factories_map.dart
.
This transformer is part of the di
package and angular calls into it.
A relative url in the templateUrl field of a @Component
annotation, poses a question - relative to what? Without any transformation it would be relative to the current page when we send the XHR to fetch it. The transformation makes it relative to the current .dart file. The transformations are recorded in <your_file>_static_type_to_uri_mapper.dart
.
Angular ships with an interpreter that evaluates your template expressions. There is no eval
in dart, so the interpreter needs to have closures to call for performing field gets and sets. For example, if you have <div>{{c = a.b}}</div>
in your template, the transformer will generate maps of getters like "a": (o) => o.a
and setters like "c": (o, v) => o.c = v
. This allows the interpreter to perform what amounts to eval("c = a.b")
.
The output of this transformer is in <your_file>_static_expressions.dart
.
Class metadata annotations like @Component
are not accessible during runtime without reflection. This transformer creates a map between types and their corresponding angular annotations. It is mostly used for @Component
and @Decorator
, and @Injectable
annotations.
For example if you write:
@Component(selector: 'cmp')
class MyComponent
it would generate the following key-value pairing
MyComponent: [const Component(selector('cmp')]
The output of this transformer is in <your_file>_static_metadata.dart
.
All the previous transformers output static structures into *_static_*.dart
files. This final step hooks the static structures into your angular application. Because transformers cannot operate on libraries (like angular), it transforms the point in your application code where you call into the applicationFactory
and replaces it with a staticApplicationFactory
that has all the static structures generated.
Missing getter: (o) => o.foo
.
This means the expression extraction did not do its job correctly. Verify the getter is present/missing from *_static_expressions.dart
. Run pub build
and look for Unable to find *.html
messages. Debug lib/tools/transformer/expression_generator.dart
. Known issues: #1592.
For example if in web/main.dart we have:
@Component(
templateUrl: 'myTemplate.html'
The transformer will look for myTemplate.html
at the root of the library, while pub serve
will serve it from web
. If you replace it with web/myTemplate.html
, the transformer will find it, but pub serve
will not serve it. PR #1652 will fix this issue.
Workarounds include:
- Using packages relative urls -
templateUrl: 'packages/my_package/path_to_template'
(omitting lib). - listing the templates in
pubspec.yaml
,
transformers:
- angular:
html_files:
- <my_undetected_file>
Currently (late 2014) pub does not support interactive debugging of transformers. Add plenty of print statements.