From 3b6aaf90543ad43c27387c4aa56c12772a35a2ee Mon Sep 17 00:00:00 2001 From: Tim Blasi Date: Fri, 20 Feb 2015 15:16:59 -0800 Subject: [PATCH] feat(dart/transform) Remove import of dart:mirrors + Precede the call to `new ReflectionCapabilities()` with our generated code which populates the reflection map statically. + Add the import of our generated code. + Once we are generating all necessary code, we will remove the import of reflection_capabilities.dart and the instantiation of `ReflectionCapabilities`, cutting the dependency on dart:mirrors. Closes #761 --- .../src/transform/annotation_processor.dart | 2 +- modules/angular2/src/transform/codegen.dart | 23 +-- .../src/transform/find_bootstrap.dart | 8 +- .../find_reflection_capabilities.dart | 152 ++++++++++++++++++ .../src/transform/html_transform.dart | 52 ------ modules/angular2/src/transform/options.dart | 41 +++-- modules/angular2/src/transform/resolvers.dart | 9 ++ .../angular2/src/transform/transformer.dart | 97 +++++------ modules/angular2/test/transform/common.html | 3 - .../expected/index.html | 3 - .../html_entry_point_files/index.dart | 6 - .../expected/index.bootstrap.dart | 6 +- .../transform/list_of_types_files/index.dart | 2 + .../transform/reflection_capabilities.dart | 9 ++ .../expected/index.bootstrap.dart | 6 +- .../expected/index.dart | 13 ++ .../simple_annotation_files/index.dart | 3 + .../expected/index.bootstrap.dart | 6 +- .../transform/synthetic_ctor_files/index.dart | 2 + .../test/transform/transform.server.spec.dart | 62 ++++--- .../expected/index.bootstrap.dart | 6 +- .../two_annotations_files/index.dart | 2 + .../expected/index.bootstrap.dart | 6 +- .../test/transform/two_deps_files/index.dart | 2 + modules/examples/pubspec.yaml | 5 +- 25 files changed, 339 insertions(+), 187 deletions(-) create mode 100644 modules/angular2/src/transform/find_reflection_capabilities.dart delete mode 100644 modules/angular2/src/transform/html_transform.dart delete mode 100644 modules/angular2/test/transform/common.html delete mode 100644 modules/angular2/test/transform/html_entry_point_files/expected/index.html delete mode 100644 modules/angular2/test/transform/html_entry_point_files/index.dart create mode 100644 modules/angular2/test/transform/reflection_capabilities.dart create mode 100644 modules/angular2/test/transform/simple_annotation_files/expected/index.dart diff --git a/modules/angular2/src/transform/annotation_processor.dart b/modules/angular2/src/transform/annotation_processor.dart index a04f31a6c2..5d56c86ede 100644 --- a/modules/angular2/src/transform/annotation_processor.dart +++ b/modules/angular2/src/transform/annotation_processor.dart @@ -54,7 +54,7 @@ class AnnotationMatcher { } } -/// [ConstructorElement] / [ElementAnnotation] pair, where the Constructor +/// [ClassElement] / [ElementAnnotation] pair. class AnnotationMatch { /// The resolved element corresponding to [node]. final ClassElement element; diff --git a/modules/angular2/src/transform/codegen.dart b/modules/angular2/src/transform/codegen.dart index 61b6fda209..10fbc46e9b 100644 --- a/modules/angular2/src/transform/codegen.dart +++ b/modules/angular2/src/transform/codegen.dart @@ -60,6 +60,12 @@ abstract class DirectiveRegistry { void register(AnnotationMatch entry); } +const setupReflectionMethodName = 'setupReflection'; + +const _libraryDeclaration = ''' +library angular2.src.transform.generated; +'''; + const _reflectorImport = ''' import 'package:angular2/src/reflection/reflection.dart' show reflector; '''; @@ -70,18 +76,17 @@ AssetId _assetIdFromLibraryElement(LibraryElement el) { return (el.source as dynamic).assetId; } -String codegenEntryPoint(Context context, - {LibraryElement entryPoint, AssetId newEntryPoint}) { - // This must be called prior to [codegenImports] or the entry point - // library will not be imported. - var entryPointPrefix = context._getPrefixDot(entryPoint); +String codegenEntryPoint(Context context, {AssetId newEntryPoint}) { + if (newEntryPoint == null) { + throw new ArgumentError.notNull('newEntryPoint'); + } // TODO(jakemac): copyright and library declaration - var outBuffer = new StringBuffer(_reflectorImport); + var outBuffer = new StringBuffer() + ..write(_libraryDeclaration) + ..write(_reflectorImport); _codegenImports(context, newEntryPoint, outBuffer); outBuffer - ..write('main() {') - ..write(context.directiveRegistry.toString()) - ..write('${entryPointPrefix}main();}'); + .write('${setupReflectionMethodName}() {${context.directiveRegistry}}'); return new DartFormatter().format(outBuffer.toString()); } diff --git a/modules/angular2/src/transform/find_bootstrap.dart b/modules/angular2/src/transform/find_bootstrap.dart index c6b9f4f52e..0d3826b23e 100644 --- a/modules/angular2/src/transform/find_bootstrap.dart +++ b/modules/angular2/src/transform/find_bootstrap.dart @@ -1,3 +1,5 @@ +library angular2.src.transform; + import 'package:analyzer/src/generated/ast.dart'; import 'package:analyzer/src/generated/element.dart'; import 'package:code_transformers/resolver.dart'; @@ -60,8 +62,8 @@ class _ParseBootstrapTypeVisitor extends SimpleAstVisitor { // TODO(kegluneq): Allow non-SimpleIdentifier expressions. @override - Object visitSimpleIdentifier(SimpleIdentifier e) { - bootstrapType = (e.bestElement as ClassElement); + Object visitSimpleIdentifier(SimpleIdentifier node) { + bootstrapType = (node.bestElement as ClassElement); if (!_types.isComponent(bootstrapType)) { throw new ArgumentError('Class passed to `${bootstrapMethodName}` must ' 'be a @${_types.componentAnnotation.name}'); @@ -71,7 +73,7 @@ class _ParseBootstrapTypeVisitor extends SimpleAstVisitor { /// Recursively visits all nodes in an Ast structure, recording all encountered /// calls to the provided [FunctionElement]. -class _FindFunctionVisitor extends UnifyingAstVisitor { +class _FindFunctionVisitor extends RecursiveAstVisitor { final FunctionElement _target; _FindFunctionVisitor(this._target); diff --git a/modules/angular2/src/transform/find_reflection_capabilities.dart b/modules/angular2/src/transform/find_reflection_capabilities.dart new file mode 100644 index 0000000000..d6c0b88229 --- /dev/null +++ b/modules/angular2/src/transform/find_reflection_capabilities.dart @@ -0,0 +1,152 @@ +library angular2.src.transform; + +import 'package:analyzer/src/generated/ast.dart'; +import 'package:analyzer/src/generated/element.dart'; +import 'package:analyzer/src/generated/java_core.dart'; +import 'package:barback/barback.dart'; +import 'package:code_transformers/resolver.dart'; +import 'package:dart_style/dart_style.dart'; +import 'package:path/path.dart' as path; + +import 'codegen.dart'; +import 'logging.dart'; +import 'resolvers.dart'; + +/// Finds all calls to the Angular2 [ReflectionCapabilities] constructor +/// defined in [library]. +/// This only searches the code defined in the file +// represented by [library], not `part`s, `import`s, `export`s, etc. +String findReflectionCapabilities( + Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) { + var types = new Angular2Types(resolver); + if (types.reflectionCapabilities == null) { + throw new ArgumentError( + 'Could not find class for ${reflectionCapabilitiesTypeName}.'); + } + + var codegen = new _SetupReflectionCodegen( + resolver, reflectionEntryPoint, newEntryPoint); + + var writer = new PrintStringWriter(); + var visitor = new _RewriteReflectionEntryPointVisitor( + writer, types.reflectionCapabilities, codegen); + + // TODO(kegluneq): Determine how to get nodes without querying Element#node. + // Root of file defining that library (main part). + resolver.getLibrary(reflectionEntryPoint).definingCompilationUnit.node + .accept(visitor); + + return new DartFormatter().format(writer.toString()); +} + +class _SetupReflectionCodegen { + static const _prefixBase = 'ngStaticInit'; + + final String prefix; + final String importUri; + + _SetupReflectionCodegen._internal(this.prefix, this.importUri); + + factory _SetupReflectionCodegen( + Resolver resolver, AssetId reflectionEntryPoint, AssetId newEntryPoint) { + var lib = resolver.getLibrary(reflectionEntryPoint); + var prefix = _prefixBase; + var idx = 0; + while (lib.imports.any((import) { + return import.prefix != null && import.prefix == prefix; + })) { + prefix = '${_prefixBase}${idx++}'; + } + + var importPath = path.relative(newEntryPoint.path, + from: path.dirname(reflectionEntryPoint.path)); + return new _SetupReflectionCodegen._internal(prefix, importPath); + } + + /// Generates code to import the library containing the method which sets up + /// Angular2 reflection statically. + /// + /// The code generated here should follow the example of code generated for + /// an [ImportDirective] node. + String codegenImport() { + return 'import \'${importUri}\' as ${prefix};'; + } + + /// Generates code to call the method which sets up Angular2 reflection + /// statically. + /// + /// The code generated here should follow the example of code generated for + /// a [MethodInvocation] node, that is, it should be prefixed as necessary + /// and not be followed by a ';'. + String codegenSetupReflectionCall() { + return '${prefix}.${setupReflectionMethodName}()'; + } +} + +class _RewriteReflectionEntryPointVisitor extends ToSourceVisitor { + final PrintWriter _writer; + final ClassElement _forbiddenClass; + final _SetupReflectionCodegen _codegen; + + _RewriteReflectionEntryPointVisitor( + PrintWriter writer, this._forbiddenClass, this._codegen) + : _writer = writer, + super(writer); + + bool _isNewReflectionCapabilities(InstanceCreationExpression node) { + var typeElement = node.constructorName.type.name.bestElement; + return typeElement != null && typeElement == _forbiddenClass; + } + + bool _isReflectionCapabilitiesImport(ImportDirective node) { + return node.uriElement == _forbiddenClass.library; + } + + @override + Object visitImportDirective(ImportDirective node) { + if (_isReflectionCapabilitiesImport(node)) { + // TODO(kegluneq): Remove newlines once dart_style bug is fixed. + // https://github.com/dart-lang/dart_style/issues/178 + // _writer.print('\n/* ReflectionCapabilities import removed */\n'); + _writer.print(_codegen.codegenImport()); + // TODO(kegluneq): Remove once we generate all needed code. + { + super.visitImportDirective(node); + } + return null; + } + return super.visitImportDirective(node); + } + + @override + Object visitAssignmentExpression(AssignmentExpression node) { + if (node.rightHandSide is InstanceCreationExpression && + _isNewReflectionCapabilities(node.rightHandSide)) { + // TODO(kegluneq): Remove newlines once dart_style bug is fixed. + // https://github.com/dart-lang/dart_style/issues/178 + // _writer.print('/* Creation of ReflectionCapabilities removed */\n'); + _writer.print(_codegen.codegenSetupReflectionCall()); + + // TODO(kegluneq): Remove once we generate all needed code. + { + _writer.print(';'); + node.leftHandSide.accept(this); + _writer.print(' ${node.operator.lexeme} '); + super.visitInstanceCreationExpression(node.rightHandSide); + } + return null; + } + return super.visitAssignmentExpression(node); + } + + @override + Object visitInstanceCreationExpression(InstanceCreationExpression node) { + if (_isNewReflectionCapabilities(node)) { + logger.error('Unexpected format in creation of ' + '${reflectionCapabilitiesTypeName}'); + } else { + return super.visitInstanceCreationExpression(node); + } + return null; + } +} diff --git a/modules/angular2/src/transform/html_transform.dart b/modules/angular2/src/transform/html_transform.dart deleted file mode 100644 index 31bec5b7b6..0000000000 --- a/modules/angular2/src/transform/html_transform.dart +++ /dev/null @@ -1,52 +0,0 @@ -library angular2.src.transform; - -import 'dart:async'; -import 'package:barback/barback.dart'; -import 'package:html5lib/dom.dart' as dom; -import 'package:html5lib/parser.dart' show parse; -import 'package:path/path.dart' as path; - -import 'options.dart'; - -Future transformHtmlEntryPoint( - TransformerOptions options, Transform transform) { - // For now at least, [options.htmlEntryPoint], [options.entryPoint], and - // [options.newEntryPoint] need to be in the same folder. - // TODO(jakemac): support package urls with [options.entryPoint] or - // [options.newEntryPoint] in `lib`, and [options.htmlEntryPoint] in another - // directory. - var _expectedDir = path.split(options.htmlEntryPoint)[0]; - if (!options.inSameTopLevelDir()) { - transform.logger.error( - '${options.htmlEntryPointParam}, ${options.entryPointParam}, and ' - '${options.newEntryPointParam} (if supplied) all must be in the ' - 'same top level directory.'); - } - - // The relative path from [options.htmlEntryPoint] to [dartEntry]. You must - // ensure that neither of these is null before calling this function. - String _relativeDartEntryPath(String dartEntry) => - path.relative(dartEntry, from: path.dirname(options.htmlEntryPoint)); - - // Checks if the src of this script tag is pointing at `options.entryPoint`. - bool _isEntryPointScript(dom.Element script) => - path.normalize(script.attributes['src']) == - _relativeDartEntryPath(options.entryPoint); - - return transform.primaryInput.readAsString().then((String html) { - var found = false; - var doc = parse(html); - var scripts = doc.querySelectorAll('script[type="application/dart"]'); - for (dom.Element script in scripts) { - if (!_isEntryPointScript(script)) continue; - script.attributes['src'] = _relativeDartEntryPath(options.newEntryPoint); - found = true; - } - if (!found) { - transform.logger.error('Unable to find script for ${options.entryPoint} ' - 'in ${options.htmlEntryPoint}.'); - } - return transform.addOutput( - new Asset.fromString(transform.primaryInput.id, doc.outerHtml)); - }); -} diff --git a/modules/angular2/src/transform/options.dart b/modules/angular2/src/transform/options.dart index 45ebbf96fb..3f64d80286 100644 --- a/modules/angular2/src/transform/options.dart +++ b/modules/angular2/src/transform/options.dart @@ -1,28 +1,41 @@ library angular2.src.transform; -import 'package:path/path.dart' as path; +const entryPointParam = 'entry_point'; +const reflectionEntryPointParam = 'reflection_entry_point'; +const newEntryPointParam = 'new_entry_point'; /// Provides information necessary to transform an Angular2 app. class TransformerOptions { /// The file where the application's call to [bootstrap] is. - // TODO(kegluenq): Allow multiple bootstrap entry points. - final String bootstrapEntryPoint; - - /// The Dart entry point, that is, where the initial call to [main] occurs. + // TODO(kegluneq): Allow multiple entry points. final String entryPoint; + /// The reflection entry point, that is, where the + /// application's [ReflectionCapabilities] are set. + final String reflectionEntryPoint; + /// The path where we should generate code. final String newEntryPoint; - /// The html file that includes [entryPoint]. - final String htmlEntryPoint; + TransformerOptions._internal( + this.entryPoint, this.reflectionEntryPoint, this.newEntryPoint); - TransformerOptions(this.bootstrapEntryPoint, this.entryPoint, - this.newEntryPoint, this.htmlEntryPoint); - - bool inSameTopLevelDir() { - var expectedDir = path.split(htmlEntryPoint)[0]; - return (expectedDir == path.split(entryPoint)[0] && - expectedDir == path.split(newEntryPoint)[0]); + factory TransformerOptions(String entryPoint, + {String reflectionEntryPoint, String newEntryPoint}) { + if (entryPoint == null || entryPoint.isEmpty) { + throw new ArgumentError.notNull(entryPointParam); + } + if (reflectionEntryPoint == null || entryPoint.isEmpty) { + reflectionEntryPoint = entryPoint; + } + if (newEntryPoint == null || newEntryPoint.isEmpty) { + newEntryPoint = + reflectionEntryPoint.replaceFirst('.dart', '.bootstrap.dart'); + if (newEntryPoint == reflectionEntryPoint) { + newEntryPoint = 'bootstrap.${newEntryPoint}'; + } + } + return new TransformerOptions._internal( + entryPoint, reflectionEntryPoint, newEntryPoint); } } diff --git a/modules/angular2/src/transform/resolvers.dart b/modules/angular2/src/transform/resolvers.dart index 45f02a27d6..bee12674d0 100644 --- a/modules/angular2/src/transform/resolvers.dart +++ b/modules/angular2/src/transform/resolvers.dart @@ -47,6 +47,7 @@ Resolvers createResolvers() { } const bootstrapMethodName = 'bootstrap'; +const reflectionCapabilitiesTypeName = 'ReflectionCapabilities'; /// Provides resolved [Elements] for well-known Angular2 symbols. class Angular2Types { @@ -57,6 +58,8 @@ class Angular2Types { new AssetId('angular2', 'lib/src/core/application.dart'); static final _templateLibAssetId = new AssetId('angular2', 'lib/src/core/annotations/template.dart'); + static final _reflectionCapabilitiesLibAssetId = new AssetId( + 'angular2', 'lib/src/reflection/reflection_capabilities.dart'); final Resolver _resolver; FunctionElement _bootstrapMethod; @@ -84,6 +87,12 @@ class Angular2Types { ClassElement get templateAnnotation => _getTypeSafe(templateLib, 'Template'); + LibraryElement get reflectionCapabilitiesLib => + _resolver.getLibrary(_reflectionCapabilitiesLibAssetId); + + ClassElement get reflectionCapabilities => + _getTypeSafe(reflectionCapabilitiesLib, reflectionCapabilitiesTypeName); + LibraryElement get applicationLib => _resolver.getLibrary(_applicationLibAssetId); diff --git a/modules/angular2/src/transform/transformer.dart b/modules/angular2/src/transform/transformer.dart index 2c11f95fd2..7393ba83d2 100644 --- a/modules/angular2/src/transform/transformer.dart +++ b/modules/angular2/src/transform/transformer.dart @@ -9,7 +9,7 @@ import 'package:code_transformers/resolver.dart'; import 'annotation_processor.dart'; import 'codegen.dart' as codegen; import 'find_bootstrap.dart'; -import 'html_transform.dart'; +import 'find_reflection_capabilities.dart'; import 'logging.dart' as log; import 'options.dart'; import 'resolvers.dart'; @@ -25,61 +25,72 @@ class AngularTransformer extends Transformer { AngularTransformer(this.options) : _resolvers = createResolvers(); - static const _bootstrapEntryPointParam = 'bootstrap_entry_point'; - static const _entryPointParam = 'entry_point'; - static const _newEntryPointParam = 'new_entry_point'; - static const _htmlEntryPointParam = 'html_entry_point'; - factory AngularTransformer.asPlugin(BarbackSettings settings) { - var bootstrapEntryPoint = settings.configuration[_bootstrapEntryPointParam]; - var entryPoint = settings.configuration[_entryPointParam]; - var newEntryPoint = settings.configuration[_newEntryPointParam]; - if (newEntryPoint == null) { - newEntryPoint = entryPoint.replaceFirst('.dart', '.bootstrap.dart'); - } - var htmlEntryPoint = settings.configuration[_htmlEntryPointParam]; + var config = settings.configuration; return new AngularTransformer(new TransformerOptions( - bootstrapEntryPoint, entryPoint, newEntryPoint, htmlEntryPoint)); + config[entryPointParam], + reflectionEntryPoint: config[reflectionEntryPointParam], + newEntryPoint: config[newEntryPointParam])); } - bool isPrimary(AssetId id) => - options.entryPoint == id.path || options.htmlEntryPoint == id.path; + bool isPrimary(AssetId id) => options.reflectionEntryPoint == id.path; Future apply(Transform transform) { log.init(transform); - if (transform.primaryInput.id.path == options.entryPoint) { - return _buildBootstrapFile(transform); - } else if (transform.primaryInput.id.path == options.htmlEntryPoint) { - return transformHtmlEntryPoint(options, transform); - } - return null; - } - - Future _buildBootstrapFile(Transform transform) { - var bootstrapEntryPointId = new AssetId( - transform.primaryInput.id.package, options.bootstrapEntryPoint); + var entryPointId = + new AssetId(transform.primaryInput.id.package, options.entryPoint); + var reflectionEntryPointId = new AssetId( + transform.primaryInput.id.package, options.reflectionEntryPoint); var newEntryPointId = new AssetId(transform.primaryInput.id.package, options.newEntryPoint); - return transform.hasInput(newEntryPointId).then((exists) { - if (exists) { + + var reflectionExists = transform.hasInput(reflectionEntryPointId); + var newEntryPointExists = transform.hasInput(newEntryPointId); + + Resolver myResolver; + return Future + .wait([reflectionExists, newEntryPointExists]) + .then((existsList) { + if (!existsList[0]) { + log.logger.error('Reflection entry point file ' + '${reflectionEntryPointId} does not exist.'); + } else if (existsList[1]) { log.logger .error('New entry point file $newEntryPointId already exists.'); } else { - return _resolvers.get(transform).then((resolver) { + return _resolvers + .get(transform, [entryPointId, reflectionEntryPointId]) + .then((resolver) { + myResolver = resolver; try { - new _BootstrapFileBuilder(resolver, transform, - transform.primaryInput.id, bootstrapEntryPointId, - newEntryPointId).run(); + String reflectionCapabilitiesCreation = findReflectionCapabilities( + resolver, reflectionEntryPointId, newEntryPointId); + + transform.addOutput(new Asset.fromString( + reflectionEntryPointId, reflectionCapabilitiesCreation)); + // Find the call to `new ReflectionCapabilities()` + // Generate new source. } catch (err, stackTrace) { log.logger.error('${err}: ${stackTrace}', - asset: bootstrapEntryPointId); + asset: reflectionEntryPointId); + rethrow; + } + + try { + new _BootstrapFileBuilder( + resolver, transform, entryPointId, newEntryPointId).run(); + } catch (err, stackTrace) { + log.logger.error('${err}: ${stackTrace}', + asset: transform.primaryInput.id); rethrow; - } finally { - resolver.release(); } }); } + }).whenComplete(() { + if (myResolver != null) { + myResolver.release(); + } }); } } @@ -87,21 +98,18 @@ class AngularTransformer extends Transformer { class _BootstrapFileBuilder { final Resolver _resolver; final Transform _transform; - final AssetId _bootstrapEntryPoint; final AssetId _entryPoint; final AssetId _newEntryPoint; _BootstrapFileBuilder(Resolver resolver, Transform transform, - this._entryPoint, this._bootstrapEntryPoint, this._newEntryPoint) + this._entryPoint, this._newEntryPoint) : _resolver = resolver, _transform = transform; /// Adds the new entry point file to the transform. Should only be ran once. void run() { - var entryLib = _resolver.getLibrary(_entryPoint); - - Set bootstrapCalls = findBootstrapCalls( - _resolver, _resolver.getLibrary(_bootstrapEntryPoint)); + Set bootstrapCalls = + findBootstrapCalls(_resolver, _resolver.getLibrary(_entryPoint)); log.logger.info('found ${bootstrapCalls.length} call(s) to `bootstrap`'); bootstrapCalls.forEach((BootstrapCallInfo info) { @@ -123,8 +131,7 @@ class _BootstrapFileBuilder { matcher.matchQueue .forEach((entry) => context.directiveRegistry.register(entry)); - _transform.addOutput(new Asset.fromString(_newEntryPoint, codegen - .codegenEntryPoint(context, - entryPoint: entryLib, newEntryPoint: _newEntryPoint))); + _transform.addOutput(new Asset.fromString(_newEntryPoint, + codegen.codegenEntryPoint(context, newEntryPoint: _newEntryPoint))); } } diff --git a/modules/angular2/test/transform/common.html b/modules/angular2/test/transform/common.html deleted file mode 100644 index 739d68575a..0000000000 --- a/modules/angular2/test/transform/common.html +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/modules/angular2/test/transform/html_entry_point_files/expected/index.html b/modules/angular2/test/transform/html_entry_point_files/expected/index.html deleted file mode 100644 index e3ded0f560..0000000000 --- a/modules/angular2/test/transform/html_entry_point_files/expected/index.html +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/modules/angular2/test/transform/html_entry_point_files/index.dart b/modules/angular2/test/transform/html_entry_point_files/index.dart deleted file mode 100644 index 83ee134e2e..0000000000 --- a/modules/angular2/test/transform/html_entry_point_files/index.dart +++ /dev/null @@ -1,6 +0,0 @@ -library web_foo; - -import 'package:angular2/src/core/annotations/annotations.dart'; -import 'package:angular2/src/core/application.dart'; - -void main() {} diff --git a/modules/angular2/test/transform/list_of_types_files/expected/index.bootstrap.dart b/modules/angular2/test/transform/list_of_types_files/expected/index.bootstrap.dart index af522abf4f..1051dff0e0 100644 --- a/modules/angular2/test/transform/list_of_types_files/expected/index.bootstrap.dart +++ b/modules/angular2/test/transform/list_of_types_files/expected/index.bootstrap.dart @@ -1,10 +1,11 @@ +library angular2.src.transform.generated; + import 'package:angular2/src/reflection/reflection.dart' show reflector; import 'bar.dart' as i0; import 'foo.dart' as i1; import 'package:angular2/src/core/annotations/annotations.dart' as i2; -import 'index.dart' as i3; -main() { +setupReflection() { reflector ..registerType(i0.MyComponent, { "factory": (i1.MyContext c) => new i0.MyComponent(c), @@ -13,5 +14,4 @@ main() { const i2.Component(componentServices: const [i1.MyContext]) ] }); - i3.main(); } diff --git a/modules/angular2/test/transform/list_of_types_files/index.dart b/modules/angular2/test/transform/list_of_types_files/index.dart index 0af996b536..505e371e0e 100644 --- a/modules/angular2/test/transform/list_of_types_files/index.dart +++ b/modules/angular2/test/transform/list_of_types_files/index.dart @@ -1,8 +1,10 @@ library web_foo; import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'bar.dart'; void main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); bootstrap(MyComponent); } diff --git a/modules/angular2/test/transform/reflection_capabilities.dart b/modules/angular2/test/transform/reflection_capabilities.dart new file mode 100644 index 0000000000..d078e801e9 --- /dev/null +++ b/modules/angular2/test/transform/reflection_capabilities.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2013, 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. +library angular2.test.transform; + +/// Mocked out version of [ReflectionCapabilities], defined in +/// src/reflection/reflection_capabilities.dart. Importing the actual file in +/// tests causes issues with resolution due to transitive dependencies. +class ReflectionCapabilities {} diff --git a/modules/angular2/test/transform/simple_annotation_files/expected/index.bootstrap.dart b/modules/angular2/test/transform/simple_annotation_files/expected/index.bootstrap.dart index ddd73afec5..a4ddc3376a 100644 --- a/modules/angular2/test/transform/simple_annotation_files/expected/index.bootstrap.dart +++ b/modules/angular2/test/transform/simple_annotation_files/expected/index.bootstrap.dart @@ -1,14 +1,14 @@ +library angular2.src.transform.generated; + import 'package:angular2/src/reflection/reflection.dart' show reflector; import 'bar.dart' as i0; import 'package:angular2/src/core/annotations/annotations.dart' as i1; -import 'index.dart' as i2; -main() { +setupReflection() { reflector ..registerType(i0.MyComponent, { "factory": () => new i0.MyComponent(), "parameters": const [const []], "annotations": const [const i1.Component(selector: '[soup]')] }); - i2.main(); } diff --git a/modules/angular2/test/transform/simple_annotation_files/expected/index.dart b/modules/angular2/test/transform/simple_annotation_files/expected/index.dart new file mode 100644 index 0000000000..cea925722d --- /dev/null +++ b/modules/angular2/test/transform/simple_annotation_files/expected/index.dart @@ -0,0 +1,13 @@ +library web_foo; + +import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection.dart'; +import 'index.bootstrap.dart' as ngStaticInit; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; +import 'bar.dart'; + +void main() { + ngStaticInit.setupReflection(); + reflector.reflectionCapabilities = new ReflectionCapabilities(); + bootstrap(MyComponent); +} diff --git a/modules/angular2/test/transform/simple_annotation_files/index.dart b/modules/angular2/test/transform/simple_annotation_files/index.dart index 0af996b536..9a72bcf857 100644 --- a/modules/angular2/test/transform/simple_annotation_files/index.dart +++ b/modules/angular2/test/transform/simple_annotation_files/index.dart @@ -1,8 +1,11 @@ library web_foo; import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection.dart'; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'bar.dart'; void main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); bootstrap(MyComponent); } diff --git a/modules/angular2/test/transform/synthetic_ctor_files/expected/index.bootstrap.dart b/modules/angular2/test/transform/synthetic_ctor_files/expected/index.bootstrap.dart index ddd73afec5..a4ddc3376a 100644 --- a/modules/angular2/test/transform/synthetic_ctor_files/expected/index.bootstrap.dart +++ b/modules/angular2/test/transform/synthetic_ctor_files/expected/index.bootstrap.dart @@ -1,14 +1,14 @@ +library angular2.src.transform.generated; + import 'package:angular2/src/reflection/reflection.dart' show reflector; import 'bar.dart' as i0; import 'package:angular2/src/core/annotations/annotations.dart' as i1; -import 'index.dart' as i2; -main() { +setupReflection() { reflector ..registerType(i0.MyComponent, { "factory": () => new i0.MyComponent(), "parameters": const [const []], "annotations": const [const i1.Component(selector: '[soup]')] }); - i2.main(); } diff --git a/modules/angular2/test/transform/synthetic_ctor_files/index.dart b/modules/angular2/test/transform/synthetic_ctor_files/index.dart index 0af996b536..505e371e0e 100644 --- a/modules/angular2/test/transform/synthetic_ctor_files/index.dart +++ b/modules/angular2/test/transform/synthetic_ctor_files/index.dart @@ -1,8 +1,10 @@ library web_foo; import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'bar.dart'; void main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); bootstrap(MyComponent); } diff --git a/modules/angular2/test/transform/transform.server.spec.dart b/modules/angular2/test/transform/transform.server.spec.dart index de9ba0d246..93e741b3c5 100644 --- a/modules/angular2/test/transform/transform.server.spec.dart +++ b/modules/angular2/test/transform/transform.server.spec.dart @@ -18,7 +18,8 @@ main() { var formatter = new DartFormatter(); var transform = new AngularTransformer(new TransformerOptions('web/index.dart', - 'web/index.dart', 'web/index.bootstrap.dart', 'web/index.html')); + reflectionEntryPoint: 'web/index.dart', + newEntryPoint: 'web/index.bootstrap.dart')); class TestConfig { final String name; @@ -32,42 +33,37 @@ class TestConfig { } void _runTests() { - // Each test has its own directory for inputs & an `expected` directory for - // expected outputs. + /* + * Each test has its own directory for inputs & an `expected` directory for + * expected outputs. + * + * In addition to these declared inputs, we inject a set of common inputs for + * every test. + */ + var commonInputs = { + 'angular2|lib/src/core/annotations/annotations.dart': + '../../lib/src/core/annotations/annotations.dart', + 'angular2|lib/src/core/application.dart': 'common.dart', + 'angular2|lib/src/reflection/reflection_capabilities.dart': + 'reflection_capabilities.dart' + }; + var tests = [ - new TestConfig('Html entry point', - inputs: { - 'a|web/index.html': 'common.html', - 'a|web/index.dart': 'html_entry_point_files/index.dart', - 'angular2|lib/src/core/annotations/annotations.dart': - '../../lib/src/core/annotations/annotations.dart', - 'angular2|lib/src/core/application.dart': 'common.dart' - }, - outputs: { - 'a|web/index.html': 'html_entry_point_files/expected/index.html' - }), new TestConfig('Simple', inputs: { - 'a|web/index.html': 'common.html', 'a|web/index.dart': 'simple_annotation_files/index.dart', - 'a|web/bar.dart': 'simple_annotation_files/bar.dart', - 'angular2|lib/src/core/annotations/annotations.dart': - '../../lib/src/core/annotations/annotations.dart', - 'angular2|lib/src/core/application.dart': 'common.dart' + 'a|web/bar.dart': 'simple_annotation_files/bar.dart' }, outputs: { 'a|web/index.bootstrap.dart': - 'simple_annotation_files/expected/index.bootstrap.dart' + 'simple_annotation_files/expected/index.bootstrap.dart', + 'a|web/index.dart': 'simple_annotation_files/expected/index.dart', }), new TestConfig('Two injected dependencies', inputs: { - 'a|web/index.html': 'common.html', 'a|web/index.dart': 'two_deps_files/index.dart', 'a|web/foo.dart': 'two_deps_files/foo.dart', - 'a|web/bar.dart': 'two_deps_files/bar.dart', - 'angular2|lib/src/core/annotations/annotations.dart': - '../../lib/src/core/annotations/annotations.dart', - 'angular2|lib/src/core/application.dart': 'common.dart' + 'a|web/bar.dart': 'two_deps_files/bar.dart' }, outputs: { 'a|web/index.bootstrap.dart': @@ -75,7 +71,6 @@ void _runTests() { }), new TestConfig('List of types', inputs: { - 'a|web/index.html': 'common.html', 'a|web/index.dart': 'list_of_types_files/index.dart', 'a|web/foo.dart': 'list_of_types_files/foo.dart', 'a|web/bar.dart': 'list_of_types_files/bar.dart', @@ -89,7 +84,6 @@ void _runTests() { }), new TestConfig('Component with synthetic Constructor', inputs: { - 'a|web/index.html': 'common.html', 'a|web/index.dart': 'synthetic_ctor_files/index.dart', 'a|web/bar.dart': 'synthetic_ctor_files/bar.dart', 'angular2|lib/src/core/annotations/annotations.dart': @@ -102,7 +96,6 @@ void _runTests() { }), new TestConfig('Component with two annotations', inputs: { - 'a|web/index.html': 'common.html', 'a|web/index.dart': 'two_annotations_files/index.dart', 'a|web/bar.dart': 'two_annotations_files/bar.dart', 'angular2|lib/src/core/annotations/annotations.dart': @@ -114,17 +107,20 @@ void _runTests() { outputs: { 'a|web/index.bootstrap.dart': 'two_annotations_files/expected/index.bootstrap.dart' - }), + }) ]; var cache = {}; for (var config in tests) { + // Read in input & output files. - config.assetPathToInputPath.forEach((key, value) { - config.assetPathToInputPath[key] = - cache.putIfAbsent(value, () => new File('test/transform/${value}').readAsStringSync()); - }); + config.assetPathToInputPath + ..addAll(commonInputs) + ..forEach((key, value) { + config.assetPathToInputPath[key] = cache.putIfAbsent(value, + () => new File('test/transform/${value}').readAsStringSync()); + }); config.assetPathToExpectedOutputPath.forEach((key, value) { config.assetPathToExpectedOutputPath[key] = cache.putIfAbsent(value, () { var code = new File('test/transform/${value}').readAsStringSync(); diff --git a/modules/angular2/test/transform/two_annotations_files/expected/index.bootstrap.dart b/modules/angular2/test/transform/two_annotations_files/expected/index.bootstrap.dart index 5ca6dde3a5..d5d665966d 100644 --- a/modules/angular2/test/transform/two_annotations_files/expected/index.bootstrap.dart +++ b/modules/angular2/test/transform/two_annotations_files/expected/index.bootstrap.dart @@ -1,10 +1,11 @@ +library angular2.src.transform.generated; + import 'package:angular2/src/reflection/reflection.dart' show reflector; import 'bar.dart' as i0; import 'package:angular2/src/core/annotations/annotations.dart' as i1; import 'package:angular2/src/core/annotations/template.dart' as i2; -import 'index.dart' as i3; -main() { +setupReflection() { reflector ..registerType(i0.MyComponent, { "factory": () => new i0.MyComponent(), @@ -14,5 +15,4 @@ main() { const i2.Template(inline: 'Salad') ] }); - i3.main(); } diff --git a/modules/angular2/test/transform/two_annotations_files/index.dart b/modules/angular2/test/transform/two_annotations_files/index.dart index 0af996b536..505e371e0e 100644 --- a/modules/angular2/test/transform/two_annotations_files/index.dart +++ b/modules/angular2/test/transform/two_annotations_files/index.dart @@ -1,8 +1,10 @@ library web_foo; import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'bar.dart'; void main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); bootstrap(MyComponent); } diff --git a/modules/angular2/test/transform/two_deps_files/expected/index.bootstrap.dart b/modules/angular2/test/transform/two_deps_files/expected/index.bootstrap.dart index 484f5bf3f3..046a923fff 100644 --- a/modules/angular2/test/transform/two_deps_files/expected/index.bootstrap.dart +++ b/modules/angular2/test/transform/two_deps_files/expected/index.bootstrap.dart @@ -1,10 +1,11 @@ +library angular2.src.transform.generated; + import 'package:angular2/src/reflection/reflection.dart' show reflector; import 'bar.dart' as i0; import 'foo.dart' as i1; import 'package:angular2/src/core/annotations/annotations.dart' as i2; -import 'index.dart' as i3; -main() { +setupReflection() { reflector ..registerType(i0.MyComponent, { "factory": @@ -12,5 +13,4 @@ main() { "parameters": const [const [i1.MyContext, String]], "annotations": const [const i2.Component(selector: i1.preDefinedSelector)] }); - i3.main(); } diff --git a/modules/angular2/test/transform/two_deps_files/index.dart b/modules/angular2/test/transform/two_deps_files/index.dart index 0af996b536..505e371e0e 100644 --- a/modules/angular2/test/transform/two_deps_files/index.dart +++ b/modules/angular2/test/transform/two_deps_files/index.dart @@ -1,8 +1,10 @@ library web_foo; import 'package:angular2/src/core/application.dart'; +import 'package:angular2/src/reflection/reflection_capabilities.dart'; import 'bar.dart'; void main() { + reflector.reflectionCapabilities = new ReflectionCapabilities(); bootstrap(MyComponent); } diff --git a/modules/examples/pubspec.yaml b/modules/examples/pubspec.yaml index c44d3f51e2..a34f9610ec 100644 --- a/modules/examples/pubspec.yaml +++ b/modules/examples/pubspec.yaml @@ -11,9 +11,8 @@ dev_dependencies: path: ../benchpress transformers: - angular2: - bootstrap_entry_point: web/src/hello_world/index_common.dart - entry_point: web/src/hello_world/index.dart - html_entry_point: web/src/hello_world/index.html + entry_point: web/src/hello_world/index_common.dart + reflection_entry_point: web/src/hello_world/index.dart - $dart2js: minify: true commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]