From 2cab7c79c3c7ed0f8863cd7401f080ae1460d06c Mon Sep 17 00:00:00 2001 From: Tim Blasi Date: Thu, 9 Apr 2015 17:49:11 -0700 Subject: [PATCH] feat(dart/transform): Allow multiple transformer entry points - Allow the user to specify multiple entry points to an app. - Allow the Angular 2 transformer to run without explicit entry points to generate necessary setters & getters on built-in directives like `For` and `If`. Closes #1246 --- .../src/transform/common/options.dart | 32 +++++++---------- .../src/transform/common/options_reader.dart | 36 +++++++++++++++++++ .../src/transform/di_transformer.dart | 16 +++++++-- .../transform/reflection_remover/codegen.dart | 21 +++++++---- .../remove_reflection_capabilities.dart | 25 +++++++------ .../reflection_remover/transformer.dart | 20 ++++++----- .../angular2/src/transform/transformer.dart | 25 +++++-------- .../test/transform/integration/all_tests.dart | 4 +-- .../expected/index.ng_deps.dart | 5 +-- .../reflection_remover/all_tests.dart | 2 +- .../expected/index.dart | 4 +-- modules/examples/pubspec.yaml | 4 +-- 12 files changed, 120 insertions(+), 74 deletions(-) create mode 100644 modules/angular2/src/transform/common/options_reader.dart diff --git a/modules/angular2/src/transform/common/options.dart b/modules/angular2/src/transform/common/options.dart index 4f63c2019d..a84fc0e2df 100644 --- a/modules/angular2/src/transform/common/options.dart +++ b/modules/angular2/src/transform/common/options.dart @@ -1,35 +1,29 @@ library angular2.transform.common.options; -const ENTRY_POINT_PARAM = 'entry_point'; -const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_point'; +const ENTRY_POINT_PARAM = 'entry_points'; +const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points'; /// Provides information necessary to transform an Angular2 app. class TransformerOptions { - /// The path to the file where the application's call to [bootstrap] is. - // TODO(kegluneq): Allow multiple entry points. - final String entryPoint; + /// The path to the files where the application's calls to `bootstrap` are. + final List entryPoints; - /// The reflection entry point, that is, the path to the file where the - /// application's [ReflectionCapabilities] are set. - final String reflectionEntryPoint; + /// The paths to the files where the application's [ReflectionCapabilities] + /// are set. + final List reflectionEntryPoints; /// The `BarbackMode#name` we are running in. final String modeName; TransformerOptions._internal( - this.entryPoint, this.reflectionEntryPoint, this.modeName); + this.entryPoints, this.reflectionEntryPoints, this.modeName); - factory TransformerOptions(String entryPoint, - {String reflectionEntryPoint, String modeName: 'release'}) { - if (entryPoint == null) { - throw new ArgumentError.notNull(ENTRY_POINT_PARAM); - } else if (entryPoint.isEmpty) { - throw new ArgumentError.value(entryPoint, 'entryPoint'); - } - if (reflectionEntryPoint == null || entryPoint.isEmpty) { - reflectionEntryPoint = entryPoint; + factory TransformerOptions(List entryPoints, + {List reflectionEntryPoints, String modeName: 'release'}) { + if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) { + reflectionEntryPoints = entryPoints; } return new TransformerOptions._internal( - entryPoint, reflectionEntryPoint, modeName); + entryPoints, reflectionEntryPoints, modeName); } } diff --git a/modules/angular2/src/transform/common/options_reader.dart b/modules/angular2/src/transform/common/options_reader.dart new file mode 100644 index 0000000000..74c479b0d6 --- /dev/null +++ b/modules/angular2/src/transform/common/options_reader.dart @@ -0,0 +1,36 @@ +library angular2.transform.common.options; + +import 'package:barback/barback.dart'; +import 'options.dart'; + +TransformerOptions parseBarbackSettings(BarbackSettings settings) { + var config = settings.configuration; + var entryPoints = _readFileList(config, ENTRY_POINT_PARAM); + var reflectionEntryPoints = + _readFileList(config, REFLECTION_ENTRY_POINT_PARAM); + return new TransformerOptions(entryPoints, + reflectionEntryPoints: reflectionEntryPoints, + modeName: settings.mode.name); +} + +/// Cribbed from the polymer project. +/// [https://github.com/dart-lang/polymer-dart] +List _readFileList(Map config, String paramName) { + var value = config[paramName]; + if (value == null) return null; + var files = []; + bool error = false; + if (value is List) { + files = value; + error = value.any((e) => e is! String); + } else if (value is String) { + files = [value]; + error = false; + } else { + error = true; + } + if (error) { + print('Invalid value for "$paramName" in the Angular 2 transformer.'); + } + return files; +} diff --git a/modules/angular2/src/transform/di_transformer.dart b/modules/angular2/src/transform/di_transformer.dart index ded855e72a..7991136dcf 100644 --- a/modules/angular2/src/transform/di_transformer.dart +++ b/modules/angular2/src/transform/di_transformer.dart @@ -8,19 +8,29 @@ import 'directive_processor/transformer.dart'; import 'bind_generator/transformer.dart'; import 'reflection_remover/transformer.dart'; import 'common/formatter.dart' as formatter; +import 'common/names.dart'; import 'common/options.dart'; +import 'common/options_reader.dart'; export 'common/options.dart'; /// Removes the mirror-based initialization logic and replaces it with static /// logic. class DiTransformerGroup extends TransformerGroup { - DiTransformerGroup() - : super([[new DirectiveProcessor(null)], [new DirectiveLinker()]]) { + DiTransformerGroup._(phases) : super(phases) { formatter.init(new DartFormatter()); } + factory DiTransformerGroup(TransformerOptions options) { + var phases = [ + [new ReflectionRemover(options)], + [new DirectiveProcessor(null)], + [new DirectiveLinker()] + ]; + return new DiTransformerGroup._(phases); + } + factory DiTransformerGroup.asPlugin(BarbackSettings settings) { - return new DiTransformerGroup(); + return new DiTransformerGroup(parseBarbackSettings(settings)); } } diff --git a/modules/angular2/src/transform/reflection_remover/codegen.dart b/modules/angular2/src/transform/reflection_remover/codegen.dart index a2ecb2ff3b..903ee4fc80 100644 --- a/modules/angular2/src/transform/reflection_remover/codegen.dart +++ b/modules/angular2/src/transform/reflection_remover/codegen.dart @@ -12,14 +12,14 @@ class Codegen { /// The prefix used to import our generated file. final String prefix; - /// The import uri - final String importUri; + /// The import uris + final Iterable importUris; - Codegen(String reflectionEntryPointPath, String newEntryPointPath, + Codegen(String reflectionEntryPointPath, Iterable newEntryPointPaths, {String prefix}) : this.prefix = prefix == null ? _PREFIX_BASE : prefix, - importUri = path.relative(newEntryPointPath, - from: path.dirname(reflectionEntryPointPath)) { + importUris = newEntryPointPaths.map((p) => + path.relative(p, from: path.dirname(reflectionEntryPointPath))) { if (this.prefix.isEmpty) throw new ArgumentError.value('(empty)', 'prefix'); } @@ -43,7 +43,10 @@ class Codegen { /// The code generated here should follow the example of code generated for /// an [ImportDirective] node. String codegenImport() { - return 'import \'${importUri}\' as ${prefix};'; + var count = 0; + return importUris + .map((importUri) => 'import \'${importUri}\' as ${prefix}${count++};') + .join(''); } /// Generates code to call the method which sets up Angular2 reflection @@ -63,7 +66,11 @@ class Codegen { reflectorExpression = 'reflector'; } - return '${prefix}.${SETUP_METHOD_NAME}(${reflectorExpression});'; + var count = 0; + return importUris + .map((_) => + '${prefix}${count++}.${SETUP_METHOD_NAME}(${reflectorExpression});') + .join(''); } } diff --git a/modules/angular2/src/transform/reflection_remover/remove_reflection_capabilities.dart b/modules/angular2/src/transform/reflection_remover/remove_reflection_capabilities.dart index 3fb9820b9f..704fe63bf9 100644 --- a/modules/angular2/src/transform/reflection_remover/remove_reflection_capabilities.dart +++ b/modules/angular2/src/transform/reflection_remover/remove_reflection_capabilities.dart @@ -1,21 +1,26 @@ library angular2.transform.reflection_remover.remove_reflection_capabilities; +import 'dart:async'; import 'package:analyzer/analyzer.dart'; +import 'package:barback/barback.dart'; +import 'package:angular2/src/transform/common/asset_reader.dart'; import 'codegen.dart'; import 'rewriter.dart'; -/// Finds the call to the Angular2 [ReflectionCapabilities] constructor -/// in [code] and replaces it with a call to `setupReflection` in -/// [newEntryPoint]. +/// Finds the call to the Angular2 `ReflectionCapabilities` constructor +/// in [reflectionEntryPoint] and replaces it with a call to +/// `setupReflection` in [newEntryPoint]. /// -/// [reflectionEntryPointPath] is the path where [code] is defined and is -/// used to display parsing errors. -/// -/// This only searches [code] not `part`s, `import`s, `export`s, etc. -String removeReflectionCapabilities( - String code, String reflectionEntryPointPath, String newEntryPointPath) { - var codegen = new Codegen(reflectionEntryPointPath, newEntryPointPath); +/// This only searches the code in [reflectionEntryPoint], not `part`s, +/// `import`s, `export`s, etc. +Future removeReflectionCapabilities(AssetReader reader, + AssetId reflectionEntryPoint, Iterable newEntryPoints) async { + var code = await reader.readAsString(reflectionEntryPoint); + var reflectionEntryPointPath = reflectionEntryPoint.path; + var newEntryPointPaths = newEntryPoints.map((id) => id.path); + + var codegen = new Codegen(reflectionEntryPointPath, newEntryPointPaths); return new Rewriter(code, codegen) .rewrite(parseCompilationUnit(code, name: reflectionEntryPointPath)); } diff --git a/modules/angular2/src/transform/reflection_remover/transformer.dart b/modules/angular2/src/transform/reflection_remover/transformer.dart index 326b4786e4..3b9a5aa58a 100644 --- a/modules/angular2/src/transform/reflection_remover/transformer.dart +++ b/modules/angular2/src/transform/reflection_remover/transformer.dart @@ -1,6 +1,7 @@ library angular2.transform.reflection_remover.transformer; import 'dart:async'; +import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/logging.dart' as log; import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/options.dart'; @@ -23,21 +24,24 @@ class ReflectionRemover extends Transformer { ReflectionRemover(this.options); @override - bool isPrimary(AssetId id) => options.reflectionEntryPoint == id.path; + bool isPrimary(AssetId id) => options.reflectionEntryPoints != null && + options.reflectionEntryPoints.contains(id.path); @override Future apply(Transform transform) async { log.init(transform); try { - var newEntryPoint = new AssetId( - transform.primaryInput.id.package, options.entryPoint) - .changeExtension(DEPS_EXTENSION); + var newEntryPoints = options.entryPoints.map((entryPoint) { + return new AssetId(transform.primaryInput.id.package, entryPoint) + .changeExtension(DEPS_EXTENSION); + }); + var reader = new AssetReader.fromTransform(transform); - var assetCode = await transform.primaryInput.readAsString(); - transform.addOutput(new Asset.fromString(transform.primaryInput.id, - removeReflectionCapabilities( - assetCode, transform.primaryInput.id.path, newEntryPoint.path))); + var transformedCode = await removeReflectionCapabilities( + reader, transform.primaryInput.id, newEntryPoints); + transform.addOutput( + new Asset.fromString(transform.primaryInput.id, transformedCode)); } catch (ex, stackTrace) { log.logger.error('Removing reflection failed.\n' 'Exception: $ex\n' diff --git a/modules/angular2/src/transform/transformer.dart b/modules/angular2/src/transform/transformer.dart index 8385c5969a..f3c4f72b9e 100644 --- a/modules/angular2/src/transform/transformer.dart +++ b/modules/angular2/src/transform/transformer.dart @@ -11,6 +11,7 @@ import 'template_compiler/transformer.dart'; import 'common/formatter.dart' as formatter; import 'common/names.dart'; import 'common/options.dart'; +import 'common/options_reader.dart'; export 'common/options.dart'; @@ -21,25 +22,17 @@ class AngularTransformerGroup extends TransformerGroup { } factory AngularTransformerGroup(TransformerOptions options) { - var phases = [[new DirectiveProcessor(options)], [new DirectiveLinker()]]; - if (options.modeName == TRANSFORM_MODE) { - phases.addAll([ - [new BindGenerator(options)], - [new TemplateCompiler(options)], - [new ReflectionRemover(options)] - ]); - } + var phases = [ + [new ReflectionRemover(options)], + [new DirectiveProcessor(options)], + [new DirectiveLinker()], + [new BindGenerator(options)], + [new TemplateCompiler(options)] + ]; return new AngularTransformerGroup._(phases); } factory AngularTransformerGroup.asPlugin(BarbackSettings settings) { - return new AngularTransformerGroup(_parseOptions(settings)); + return new AngularTransformerGroup(parseBarbackSettings(settings)); } } - -TransformerOptions _parseOptions(BarbackSettings settings) { - var config = settings.configuration; - return new TransformerOptions(config[ENTRY_POINT_PARAM], - reflectionEntryPoint: config[REFLECTION_ENTRY_POINT_PARAM], - modeName: settings.mode.name); -} diff --git a/modules/angular2/test/transform/integration/all_tests.dart b/modules/angular2/test/transform/integration/all_tests.dart index 306a9eba17..ab770932ab 100644 --- a/modules/angular2/test/transform/integration/all_tests.dart +++ b/modules/angular2/test/transform/integration/all_tests.dart @@ -11,8 +11,8 @@ import '../common/read_file.dart'; var formatter = new DartFormatter(); var transform = new AngularTransformerGroup(new TransformerOptions( - 'web/index.dart', - reflectionEntryPoint: 'web/index.dart', modeName: TRANSFORM_MODE)); + ['web/index.dart'], + reflectionEntryPoints: ['web/index.dart'], modeName: TRANSFORM_MODE)); class IntegrationTestConfig { final String name; diff --git a/modules/angular2/test/transform/integration/simple_annotation_files/expected/index.ng_deps.dart b/modules/angular2/test/transform/integration/simple_annotation_files/expected/index.ng_deps.dart index b357acdb04..fa799430e0 100644 --- a/modules/angular2/test/transform/integration/simple_annotation_files/expected/index.ng_deps.dart +++ b/modules/angular2/test/transform/integration/simple_annotation_files/expected/index.ng_deps.dart @@ -3,12 +3,10 @@ library web_foo.ng_deps.dart; import 'index.dart'; import 'package:angular2/src/core/application.dart'; import 'package:angular2/src/reflection/reflection.dart'; -import 'package:angular2/src/reflection/reflection_capabilities.dart'; +import 'index.ng_deps.dart' as ngStaticInit0; import 'bar.dart'; import 'bar.ng_deps.dart' as i0; import 'package:angular2/src/core/application.ng_deps.dart' as i1; -import 'package:angular2/src/reflection/reflection_capabilities.ng_deps.dart' - as i2; bool _visited = false; void initReflector(reflector) { @@ -16,5 +14,4 @@ void initReflector(reflector) { _visited = true; i0.initReflector(reflector); i1.initReflector(reflector); - i2.initReflector(reflector); } diff --git a/modules/angular2/test/transform/reflection_remover/all_tests.dart b/modules/angular2/test/transform/reflection_remover/all_tests.dart index e943514bd4..25d6ab79ca 100644 --- a/modules/angular2/test/transform/reflection_remover/all_tests.dart +++ b/modules/angular2/test/transform/reflection_remover/all_tests.dart @@ -9,7 +9,7 @@ import 'reflection_remover_files/expected/index.dart' as expected; import '../common/read_file.dart'; void allTests() { - var codegen = new Codegen('web/index.dart', 'web/index.ng_deps.dart'); + var codegen = new Codegen('web/index.dart', ['web/index.ng_deps.dart']); it('should remove uses of mirrors & insert calls to generated code.', () { var code = diff --git a/modules/angular2/test/transform/reflection_remover/reflection_remover_files/expected/index.dart b/modules/angular2/test/transform/reflection_remover/reflection_remover_files/expected/index.dart index db2b039899..09833f1113 100644 --- a/modules/angular2/test/transform/reflection_remover/reflection_remover_files/expected/index.dart +++ b/modules/angular2/test/transform/reflection_remover/reflection_remover_files/expected/index.dart @@ -13,10 +13,10 @@ 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 'index.ng_deps.dart' as ngStaticInit; +/*import 'package:angular2/src/reflection/reflection_capabilities.dart';*/import 'index.ng_deps.dart' as ngStaticInit0; void main() { - /*reflector.reflectionCapabilities = new ReflectionCapabilities();*/ngStaticInit.initReflector(reflector); + /*reflector.reflectionCapabilities = new ReflectionCapabilities();*/ngStaticInit0.initReflector(reflector); bootstrap(MyComponent); } """; diff --git a/modules/examples/pubspec.yaml b/modules/examples/pubspec.yaml index c799570f0a..519b5d7fad 100644 --- a/modules/examples/pubspec.yaml +++ b/modules/examples/pubspec.yaml @@ -13,8 +13,8 @@ dev_dependencies: path: ../benchpress transformers: - angular2: - entry_point: web/src/hello_world/index_common.dart - reflection_entry_point: web/src/hello_world/index.dart + entry_points: web/src/hello_world/index_common.dart + reflection_entry_points: web/src/hello_world/index.dart - $dart2js: minify: true commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]