diff --git a/example/web/main.dart b/example/web/main.dart index 1a56cb5..3cb7baa 100644 --- a/example/web/main.dart +++ b/example/web/main.dart @@ -1,5 +1,4 @@ import 'package:di/di.dart'; -import 'package:di/di_dynamic.dart'; import 'dart:html'; @Injectable() @@ -10,7 +9,6 @@ class Application { } main() { - setupModuleTypeReflector(); Module module = new Module(); module.bind(Application); new ModuleInjector([module]).get(Application).run(); diff --git a/lib/module_transformer.dart b/lib/module_transformer.dart new file mode 100644 index 0000000..1e2d5a1 --- /dev/null +++ b/lib/module_transformer.dart @@ -0,0 +1,40 @@ +library di.transformer.export_transformer; + +import 'dart:async'; +import 'package:analyzer/src/generated/ast.dart'; +import 'package:analyzer/src/generated/element.dart'; +import 'package:barback/barback.dart'; +import 'package:code_transformers/resolver.dart'; +import 'package:path/path.dart' as path; + +/** + * Pub transformer that changes reflector in Module to null instead of importing + * the dynamic reflector which imports mirrors. InjectorGenerator in transformer.dart + * will change DEFAULT_REFLECTOR back to static (it is run by the app, whereas this is run by DI). + */ +class ModuleTransformerGroup implements TransformerGroup { + final Iterable phases; + + ModuleTransformerGroup.asPlugin(BarbackSettings settings) + : phases = [[new ModuleTransformer()]]; +} + +class ModuleTransformer extends Transformer { + + ModuleTransformer(); + + isPrimary(AssetId id) { + return new Future.value(id == new AssetId.parse("di|lib/src/module.dart")); + } + + Future apply(Transform transform) { + var id = transform.primaryInput.id; + return transform.primaryInput.readAsString().then((code) { + // Note: this rewrite is coupled with how module.dart is + // written. Make sure both are updated in sync. + transform.addOutput(new Asset.fromString(id, code + .replaceAll(new RegExp('import "reflector_dynamic.dart";'), + 'import "reflector_null.dart";'))); + }); + } +} diff --git a/lib/src/module.dart b/lib/src/module.dart index c4c86ca..2c4bc57 100644 --- a/lib/src/module.dart +++ b/lib/src/module.dart @@ -3,6 +3,7 @@ library di.module; import "../key.dart"; import "../check_bind_args.dart" show checkBindArgs; import "reflector.dart"; +import "reflector_dynamic.dart"; DEFAULT_VALUE(_) => null; IDENTITY(p) => p; @@ -53,7 +54,7 @@ bool isNotSet(val) => identical(val, DEFAULT_VALUE); * no effect on that injector. */ class Module { - static TypeReflector DEFAULT_REFLECTOR = new NullReflector(); + static TypeReflector DEFAULT_REFLECTOR = getReflector(); final TypeReflector reflector; Module(): reflector = DEFAULT_REFLECTOR; diff --git a/lib/src/reflector.dart b/lib/src/reflector.dart index 3a96189..d6684d6 100644 --- a/lib/src/reflector.dart +++ b/lib/src/reflector.dart @@ -32,18 +32,3 @@ abstract class TypeReflector { void addAll(Map factories, Map> parameterKeys); void add(Type type, Function factory, List parameterKeys); } - -class NullReflector extends TypeReflector { - factoryFor(Type type) => throw new NullReflectorError(); - parameterKeysFor(Type type) => throw new NullReflectorError(); - addAll(Map factories, Map> parameterKeys) => - throw new NullReflectorError(); - add(Type type, Function factory, List parameterKeys) => - throw new NullReflectorError(); -} - -class NullReflectorError extends BaseError { - NullReflectorError() - : super("Module.DEFAULT_REFLECTOR not initialized for dependency injection." - "http://goo.gl/XFXx9G"); -} diff --git a/lib/di_dynamic.dart b/lib/src/reflector_dynamic.dart similarity index 98% rename from lib/di_dynamic.dart rename to lib/src/reflector_dynamic.dart index 1d77cb5..a6d8d73 100644 --- a/lib/di_dynamic.dart +++ b/lib/src/reflector_dynamic.dart @@ -1,9 +1,9 @@ -library di.dynamic_type_factories; +library di.reflector_dynamic; -import 'di.dart'; -import 'src/mirrors.dart'; +import '../di.dart'; +import 'mirrors.dart'; -TypeReflector setupModuleTypeReflector() => Module.DEFAULT_REFLECTOR = new DynamicTypeFactories(); +TypeReflector getReflector() => new DynamicTypeFactories(); class DynamicTypeFactories extends TypeReflector { /// caches of results calculated from mirroring diff --git a/lib/src/reflector_null.dart b/lib/src/reflector_null.dart new file mode 100644 index 0000000..5a6d8df --- /dev/null +++ b/lib/src/reflector_null.dart @@ -0,0 +1,22 @@ +library di.reflector_null; + +import '../key.dart'; +import 'reflector.dart'; +import 'errors.dart'; + +TypeReflector getReflector() => new NullReflector(); + +class NullReflector extends TypeReflector { + factoryFor(Type type) => throw new NullReflectorError(); + parameterKeysFor(Type type) => throw new NullReflectorError(); + addAll(Map factories, Map> parameterKeys) => + throw new NullReflectorError(); + add(Type type, Function factory, List parameterKeys) => + throw new NullReflectorError(); +} + +class NullReflectorError extends BaseError { + NullReflectorError() + : super("Module.DEFAULT_REFLECTOR not initialized for dependency injection." + "http://goo.gl/XFXx9G"); +} diff --git a/lib/di_static.dart b/lib/src/reflector_static.dart similarity index 94% rename from lib/di_static.dart rename to lib/src/reflector_static.dart index 2bc37c0..3aab5ff 100644 --- a/lib/di_static.dart +++ b/lib/src/reflector_static.dart @@ -1,6 +1,6 @@ -library di.generated_type_factories; +library di.reflector_static; -import 'di.dart'; +import '../di.dart'; class GeneratedTypeFactories extends TypeReflector { diff --git a/lib/transformer.dart b/lib/transformer.dart index 3225270..7f3dd9d 100644 --- a/lib/transformer.dart +++ b/lib/transformer.dart @@ -11,13 +11,11 @@ * and outputted to a file [entry_point_name]_generated_type_factory_maps.dart. Multiple * entry points (main functions) is not supported. * - * The import in main is also modified to use import di_static.dart instead - * of di_dynamic.dart. + * This transformer also sets up the static reflector, since the dynamic version will have + * been erased by ModuleTransformer. * - * All of the above is taken care of by the transformer. The user needs to call - * setupModuleTypeReflector in main, before any modules are initialized. User must also - * annotate types for the transformer to add them to the generated type factories file, - * in addition to enabling the transformer in pubspec.yaml. + * User must annotate types for the transformer to add them to the generated type factories file, + * and enable the transformer in pubspec.yaml. * * Types which are considered injectable can be annotated in the following ways: * @@ -90,10 +88,10 @@ class DependencyInjectorTransformerGroup implements TransformerGroup { final Iterable phases; DependencyInjectorTransformerGroup(TransformOptions options) - : phases = _createPhases(options); + : phases = _createPhases(options); DependencyInjectorTransformerGroup.asPlugin(BarbackSettings settings) - : this(_parseSettings(settings.configuration)); + : this(_parseSettings(settings.configuration)); } TransformOptions _parseSettings(Map args) { diff --git a/lib/transformer/injector_generator.dart b/lib/transformer/injector_generator.dart index 9b9e0fe..5ebe47e 100644 --- a/lib/transformer/injector_generator.dart +++ b/lib/transformer/injector_generator.dart @@ -72,7 +72,7 @@ class _Processor { transform.addOutput( new Asset.fromString(_generatedAssetId, injectLibContents)); - _editImport(); + _editMain(); } /** Resolves the classes for the injectable annotations in the current AST. */ @@ -350,7 +350,7 @@ class _Processor { outputBuffer.write('};\nfinal Map> parameterKeys = {\n'); outputBuffer.write(paramsBuffer); outputBuffer.write('};\n'); - outputBuffer.write('setupModuleTypeReflector() => ' + outputBuffer.write('setStaticReflectorAsDefault() => ' 'Module.DEFAULT_REFLECTOR = ' 'new GeneratedTypeFactories(typeFactories, parameterKeys);\n'); @@ -362,26 +362,31 @@ class _Processor { span: resolver.getSourceSpan(element)); } - /// edits out di_dynamic import and replace with generated_type_factory_maps - void _editImport() { + /// import generated_type_factory_maps and call initialize + void _editMain() { AssetId id = transform.primaryInput.id; var lib = resolver.getLibrary(id); var unit = lib.definingCompilationUnit.node; var transaction = resolver.createTextEditTransaction(lib); + var imports = unit.directives.where((d) => d is ImportDirective); - var dir = imports.where((ImportDirective d) => - d.uriContent == 'package:di/di_dynamic.dart'); - var begin, end; - if (dir.isNotEmpty) { - begin = dir.first.offset; - end = dir.first.end; - } else { - begin = imports.last.end; - end = imports.last.end; - } - transaction.edit(begin, end, (begin == end ? "\n" : "") + 'import ' + transaction.edit(imports.last.end, imports.last.end, '\nimport ' "'${path.url.basenameWithoutExtension(id.path)}" - "_generated_type_factory_maps.dart' show setupModuleTypeReflector;"); + "_generated_type_factory_maps.dart' show setStaticReflectorAsDefault;"); + + FunctionExpression main = unit.declarations.where((d) => + d is FunctionDeclaration && d.name.toString() == 'main') + .first.functionExpression; + var body = main.body; + if (body is BlockFunctionBody) { + var location = body.beginToken.end; + transaction.edit(location, location, '\n setStaticReflectorAsDefault();'); + } else if (body is ExpressionFunctionBody) { + transaction.edit(body.beginToken.offset, body.endToken.end, + "{\n setStaticReflectorAsDefault();\n" + " return ${body.expression};\n}"); + } // EmptyFunctionBody can only appear as abstract methods and constructors. + var printer = transaction.commit(); var url = id.path.startsWith('lib/') ? 'package:${id.package}/${id.path.substring(4)}' : id.path; @@ -397,7 +402,7 @@ void _writeHeader(AssetId id, StringSink sink) { library ${id.package}.$libName.generated_type_factory_maps; import 'package:di/di.dart'; -import 'package:di/di_static.dart'; +import 'package:di/src/reflector_static.dart'; '''); } diff --git a/lib/transformer/options.dart b/lib/transformer/options.dart index f2f1952..7fa5c7e 100644 --- a/lib/transformer/options.dart +++ b/lib/transformer/options.dart @@ -35,14 +35,14 @@ class TransformOptions { final String sdkDirectory; TransformOptions({EntryFilter entryFilter, String sdkDirectory, - List injectableAnnotations, List injectedTypes}) - : entryFilter = entryFilter != null ? entryFilter : isPossibleDartEntry, - sdkDirectory = sdkDirectory, - injectableAnnotations = - (injectableAnnotations != null ? injectableAnnotations : []) - ..addAll(DEFAULT_INJECTABLE_ANNOTATIONS), - injectedTypes = - new Set.from(injectedTypes != null ? injectedTypes : []) { + List injectableAnnotations, List injectedTypes}) + : entryFilter = entryFilter != null ? entryFilter : isPossibleDartEntry, + sdkDirectory = sdkDirectory, + injectableAnnotations = + (injectableAnnotations != null ? injectableAnnotations : []) + ..addAll(DEFAULT_INJECTABLE_ANNOTATIONS), + injectedTypes = + new Set.from(injectedTypes != null ? injectedTypes : []) { if (sdkDirectory == null) throw new ArgumentError('sdkDirectory must be provided.'); } diff --git a/pubspec.yaml b/pubspec.yaml index 386fb3c..46ed18e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: di -version: 2.0.0-alpha.7 +version: 2.0.0-alpha.9 authors: - Vojta Jina - Pavel Jbanov @@ -18,3 +18,5 @@ dev_dependencies: benchmark_harness: ">=1.0.4 <2.0.0" browser: ">=0.10.0 <0.11.0" guinness: ">=0.1.10 <0.2.0" +transformers: +- di/module_transformer diff --git a/test/main.dart b/test/main.dart index 9e54a4b..4cbb6a0 100644 --- a/test/main.dart +++ b/test/main.dart @@ -15,8 +15,8 @@ import 'package:guinness/guinness.dart'; import 'package:matcher/matcher.dart' as matcher; import 'package:di/di.dart'; import 'package:di/annotations.dart'; -import 'package:di/di_static.dart'; -import 'package:di/di_dynamic.dart'; +import 'package:di/src/reflector_static.dart'; +import 'package:di/src/reflector_dynamic.dart'; import 'package:di/check_bind_args.dart'; import 'package:di/src/module.dart'; diff --git a/test/transformer_test.dart b/test/transformer_test.dart index 3f469c2..dde949f 100644 --- a/test/transformer_test.dart +++ b/test/transformer_test.dart @@ -677,10 +677,8 @@ main() { 'a|web/main.dart': ''' library main; import 'package:di/di.dart'; -import 'package:di/di_dynamic.dart'; main() { - setupModuleTypeReflector(); print('abc'); }''' }, @@ -688,10 +686,10 @@ main() { 'a|web/main.dart': ''' library main; import 'package:di/di.dart'; -import 'main_generated_type_factory_maps.dart' show setupModuleTypeReflector; +import 'main_generated_type_factory_maps.dart' show setStaticReflectorAsDefault; main() { - setupModuleTypeReflector(); + setStaticReflectorAsDefault(); print('abc'); }''' }); @@ -723,7 +721,7 @@ final Map typeFactories = { ${factories.join('')}}; final Map> parameterKeys = { ${paramKeys.join('')}}; -setupModuleTypeReflector() => Module.DEFAULT_REFLECTOR = new GeneratedTypeFactories(typeFactories, parameterKeys); +setStaticReflectorAsDefault() => Module.DEFAULT_REFLECTOR = new GeneratedTypeFactories(typeFactories, parameterKeys); ''', }, messages: messages); @@ -733,7 +731,7 @@ const String IMPORTS = ''' library a.web.main.generated_type_factory_maps; import 'package:di/di.dart'; -import 'package:di/di_static.dart'; +import 'package:di/src/reflector_static.dart'; '''; const String CLASS_ENGINE = '''