Skip to content

Commit 09948f4

Browse files
author
Tim Blasi
committed
feat(dart/transform): Add a di transformer
Add a transformer for `di` which generates `.ng_deps.dart` files for all `.dart` files it is run on. These `.ng_deps.dart` files register metadata for any `@Injectable` classes. Fix unit tests for changes introduced by the di transformer. When using `pub (build|serve) --mode=ngstatic`, we will also generate getters and setters, parse templates, and remove import of `dart:mirrors` in the Angular transform. Because this is still relatively immature, we use the mode to keep it opt-in for now. Closes angular#700
1 parent 788461b commit 09948f4

File tree

39 files changed

+147
-73
lines changed

39 files changed

+147
-73
lines changed

modules/angular2/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ dependencies:
1515
dart_style: '^0.1.3'
1616
html: '^0.12.0'
1717
stack_trace: '^1.1.1'
18+
transformers:
19+
- angular2/src/transform/di_transformer
1820
dev_dependencies:
1921
guinness: "^0.1.17"

modules/angular2/src/transform/common/names.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ const REGISTER_TYPE_METHOD_NAME = 'registerType';
77
const REGISTER_GETTERS_METHOD_NAME = 'registerGetters';
88
const REGISTER_SETTERS_METHOD_NAME = 'registerSetters';
99
const REGISTER_METHODS_METHOD_NAME = 'registerMethods';
10+
const TRANSFORM_MODE = 'ngstatic';

modules/angular2/src/transform/common/options.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,14 @@ class TransformerOptions {
1313
/// application's [ReflectionCapabilities] are set.
1414
final String reflectionEntryPoint;
1515

16-
TransformerOptions._internal(this.entryPoint, this.reflectionEntryPoint);
16+
/// The `BarbackMode#name` we are running in.
17+
final String modeName;
1718

18-
factory TransformerOptions(String entryPoint, {String reflectionEntryPoint}) {
19+
TransformerOptions._internal(
20+
this.entryPoint, this.reflectionEntryPoint, this.modeName);
21+
22+
factory TransformerOptions(String entryPoint,
23+
{String reflectionEntryPoint, String modeName: 'release'}) {
1924
if (entryPoint == null) {
2025
throw new ArgumentError.notNull(ENTRY_POINT_PARAM);
2126
} else if (entryPoint.isEmpty) {
@@ -24,6 +29,7 @@ class TransformerOptions {
2429
if (reflectionEntryPoint == null || entryPoint.isEmpty) {
2530
reflectionEntryPoint = entryPoint;
2631
}
27-
return new TransformerOptions._internal(entryPoint, reflectionEntryPoint);
32+
return new TransformerOptions._internal(
33+
entryPoint, reflectionEntryPoint, modeName);
2834
}
2935
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library angular2.src.transform.di_transformer;
2+
3+
import 'package:barback/barback.dart';
4+
import 'package:dart_style/dart_style.dart';
5+
6+
import 'directive_linker/transformer.dart';
7+
import 'directive_processor/transformer.dart';
8+
import 'bind_generator/transformer.dart';
9+
import 'reflection_remover/transformer.dart';
10+
import 'common/formatter.dart' as formatter;
11+
import 'common/options.dart';
12+
13+
export 'common/options.dart';
14+
15+
/// Removes the mirror-based initialization logic and replaces it with static
16+
/// logic.
17+
class DiTransformerGroup extends TransformerGroup {
18+
DiTransformerGroup()
19+
: super([[new DirectiveProcessor(null)], [new DirectiveLinker()]]) {
20+
formatter.init(new DartFormatter());
21+
}
22+
23+
factory DiTransformerGroup.asPlugin(BarbackSettings settings) {
24+
return new DiTransformerGroup();
25+
}
26+
}

modules/angular2/src/transform/directive_linker/transformer.dart

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ import 'linker.dart';
1616
/// `setupReflection` call the necessary `setupReflection` method in all
1717
/// dependencies.
1818
class DirectiveLinker extends Transformer {
19-
final TransformerOptions options;
20-
21-
DirectiveLinker(this.options);
19+
DirectiveLinker();
2220

2321
@override
2422
bool isPrimary(AssetId id) => id.path.endsWith(DEPS_EXTENSION);
@@ -28,10 +26,11 @@ class DirectiveLinker extends Transformer {
2826
log.init(transform);
2927

3028
try {
29+
var reader = new AssetReader.fromTransform(transform);
3130
var assetId = transform.primaryInput.id;
32-
var transformedCode =
33-
await linkNgDeps(new AssetReader.fromTransform(transform), assetId);
34-
var formattedCode = formatter.format(transformedCode, uri: assetId.path);
31+
var assetPath = assetId.path;
32+
var transformedCode = await linkNgDeps(reader, assetId);
33+
var formattedCode = formatter.format(transformedCode, uri: assetPath);
3534
transform.addOutput(new Asset.fromString(assetId, formattedCode));
3635
} catch (ex, stackTrace) {
3736
log.logger.error('Linking ng directives failed.\n'

modules/angular2/src/transform/directive_processor/rewriter.dart

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,13 @@ import 'visitors.dart';
1616
/// If no Angular 2 `Directive`s are found in [code], returns the empty
1717
/// string unless [forceGenerate] is true, in which case an empty ngDeps
1818
/// file is created.
19-
String createNgDeps(String code, String path, {bool forceGenerate: false}) {
19+
String createNgDeps(String code, String path) {
2020
// TODO(kegluneq): Shortcut if we can determine that there are no
21-
// [Directive]s present.
21+
// [Directive]s present, taking into account `export`s.
2222
var writer = new PrintStringWriter();
2323
var visitor = new CreateNgDepsVisitor(writer, path);
2424
parseCompilationUnit(code, name: path).accept(visitor);
25-
if (visitor.foundNgDirectives || forceGenerate) {
26-
return writer.toString();
27-
} else {
28-
return '';
29-
}
25+
return '$writer';
3026
}
3127

3228
/// Visitor responsible for processing [CompilationUnit] and creating an
@@ -35,8 +31,8 @@ class CreateNgDepsVisitor extends Object
3531
with SimpleAstVisitor<Object>, VisitorMixin {
3632
final PrintWriter writer;
3733
final _Tester _tester = const _Tester();
38-
bool foundNgDirectives = false;
39-
bool wroteImport = false;
34+
bool _foundNgDirectives = false;
35+
bool _wroteImport = false;
4036
final ToSourceVisitor _copyVisitor;
4137
final FactoryTransformVisitor _factoryVisitor;
4238
final ParameterTransformVisitor _paramsVisitor;
@@ -61,33 +57,35 @@ class CreateNgDepsVisitor extends Object
6157
return null;
6258
}
6359

64-
void _writeImport() {
60+
/// Write the import to the file the .ng_deps.dart file is based on if it
61+
/// has not yet been written.
62+
void _maybeWriteImport() {
63+
if (_wroteImport) return;
64+
_wroteImport = true;
6565
writer.print('''import '${path.basename(importPath)}';''');
6666
}
6767

6868
@override
6969
Object visitImportDirective(ImportDirective node) {
70-
if (!wroteImport) {
71-
_writeImport();
72-
wroteImport = true;
73-
}
70+
_maybeWriteImport();
7471
return node.accept(_copyVisitor);
7572
}
7673

7774
@override
7875
Object visitExportDirective(ExportDirective node) {
76+
_maybeWriteImport();
7977
return node.accept(_copyVisitor);
8078
}
8179

8280
void _openFunctionWrapper() {
83-
// TODO(kegluneq): Use a [PrintWriter] with a length getter.
81+
_maybeWriteImport();
8482
writer.print('bool _visited = false;'
8583
'void ${SETUP_METHOD_NAME}(${REFLECTOR_VAR_NAME}) {'
8684
'if (_visited) return; _visited = true;');
8785
}
8886

8987
void _closeFunctionWrapper() {
90-
if (foundNgDirectives) {
88+
if (_foundNgDirectives) {
9189
writer.print(';');
9290
}
9391
writer.print('}');
@@ -135,10 +133,10 @@ class CreateNgDepsVisitor extends Object
135133
if (shouldProcess) {
136134
var ctor = _getCtor(node);
137135

138-
if (!foundNgDirectives) {
136+
if (!_foundNgDirectives) {
139137
// The receiver for cascaded calls.
140138
writer.print(REFLECTOR_VAR_NAME);
141-
foundNgDirectives = true;
139+
_foundNgDirectives = true;
142140
}
143141
writer.print('..registerType(');
144142
visitNode(node.name);
@@ -168,7 +166,14 @@ class CreateNgDepsVisitor extends Object
168166
}
169167

170168
@override
171-
Object visitLibraryDirective(LibraryDirective node) => _nodeToSource(node);
169+
Object visitLibraryDirective(LibraryDirective node) {
170+
if (node != null && node.name != null) {
171+
writer.print('library ');
172+
_nodeToSource(node.name);
173+
writer.print('$DEPS_EXTENSION;');
174+
}
175+
return null;
176+
}
172177

173178
@override
174179
Object visitPartOfDirective(PartOfDirective node) {
@@ -194,6 +199,7 @@ class _Tester {
194199
var metaName = meta.name.toString();
195200
return metaName == 'Component' ||
196201
metaName == 'Decorator' ||
202+
metaName == 'Injectable' ||
197203
metaName == 'Template';
198204
}
199205
}

modules/angular2/src/transform/directive_processor/transformer.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,13 @@ class DirectiveProcessor extends Transformer {
3232
log.init(transform);
3333

3434
try {
35-
var assetCode = await transform.primaryInput.readAsString();
36-
var ngDepsSrc = createNgDeps(assetCode, transform.primaryInput.id.path,
37-
forceGenerate: transform.primaryInput.id.path == options.entryPoint);
35+
var asset = transform.primaryInput;
36+
var assetCode = await asset.readAsString();
37+
var ngDepsSrc = createNgDeps(assetCode, asset.id.path);
3838
if (ngDepsSrc != null && ngDepsSrc.isNotEmpty) {
3939
var ngDepsAssetId =
4040
transform.primaryInput.id.changeExtension(DEPS_EXTENSION);
41-
var exists = await transform.hasInput(ngDepsAssetId);
42-
if (exists) {
41+
if (await transform.hasInput(ngDepsAssetId)) {
4342
log.logger.error('Clobbering ${ngDepsAssetId}. '
4443
'This probably will not end well');
4544
}

modules/angular2/src/transform/directive_processor/visitors.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ class _CtorTransformVisitor extends ToSourceVisitor with VisitorMixin {
2929
var suffix = type != null ? ', ' : '';
3030
visitNodeListWithSeparatorAndSuffix(metadata, ', ', suffix);
3131
}
32-
if (_withParameterTypes) {
33-
visitNodeWithSuffix(type, ' ');
32+
var needCompileTimeConstants = !_withParameterNames;
33+
if (_withParameterTypes && type != null) {
34+
visitNodeWithSuffix(type.name, ' ');
35+
if (!needCompileTimeConstants) {
36+
// Types with arguments are not compile-time constants.
37+
visitNodeWithSuffix(type.typeArguments, ' ');
38+
}
3439
}
3540
if (_withParameterNames) {
3641
visitNode(name);

modules/angular2/src/transform/reflection_remover/rewriter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class Rewriter {
8181

8282
String _commentedNode(AstNode node) {
8383
// TODO(kegluneq): Return commented code once we generate all needed code.
84-
return _code.substring(node.offset, node.end);
84+
return '/*${_code.substring(node.offset, node.end)}*/';
8585
}
8686
}
8787

modules/angular2/src/transform/transformer.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,27 @@ import 'bind_generator/transformer.dart';
99
import 'reflection_remover/transformer.dart';
1010
import 'template_compiler/transformer.dart';
1111
import 'common/formatter.dart' as formatter;
12+
import 'common/names.dart';
1213
import 'common/options.dart';
1314

1415
export 'common/options.dart';
1516

1617
/// Replaces Angular 2 mirror use with generated code.
1718
class AngularTransformerGroup extends TransformerGroup {
18-
AngularTransformerGroup(TransformerOptions options) : super([
19-
[new DirectiveProcessor(options)],
20-
[new DirectiveLinker(options)],
19+
AngularTransformerGroup._(phases) : super(phases) {
20+
formatter.init(new DartFormatter());
21+
}
22+
23+
factory AngularTransformerGroup(TransformerOptions options) {
24+
var phases = [[new DirectiveProcessor(options)], [new DirectiveLinker()]];
25+
if (options.modeName == TRANSFORM_MODE) {
26+
phases.addAll([
2127
[new BindGenerator(options)],
2228
[new TemplateComplier(options)],
2329
[new ReflectionRemover(options)]
24-
]) {
25-
formatter.init(new DartFormatter());
30+
]);
31+
}
32+
return new AngularTransformerGroup._(phases);
2633
}
2734

2835
factory AngularTransformerGroup.asPlugin(BarbackSettings settings) {
@@ -33,5 +40,6 @@ class AngularTransformerGroup extends TransformerGroup {
3340
TransformerOptions _parseOptions(BarbackSettings settings) {
3441
var config = settings.configuration;
3542
return new TransformerOptions(config[ENTRY_POINT_PARAM],
36-
reflectionEntryPoint: config[REFLECTION_ENTRY_POINT_PARAM]);
43+
reflectionEntryPoint: config[REFLECTION_ENTRY_POINT_PARAM],
44+
modeName: settings.mode.name);
3745
}

0 commit comments

Comments
 (0)