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
This commit is contained in:
parent
bba849909c
commit
2cab7c79c3
|
@ -1,35 +1,29 @@
|
||||||
library angular2.transform.common.options;
|
library angular2.transform.common.options;
|
||||||
|
|
||||||
const ENTRY_POINT_PARAM = 'entry_point';
|
const ENTRY_POINT_PARAM = 'entry_points';
|
||||||
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_point';
|
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
|
||||||
|
|
||||||
/// Provides information necessary to transform an Angular2 app.
|
/// Provides information necessary to transform an Angular2 app.
|
||||||
class TransformerOptions {
|
class TransformerOptions {
|
||||||
/// The path to the file where the application's call to [bootstrap] is.
|
/// The path to the files where the application's calls to `bootstrap` are.
|
||||||
// TODO(kegluneq): Allow multiple entry points.
|
final List<String> entryPoints;
|
||||||
final String entryPoint;
|
|
||||||
|
|
||||||
/// The reflection entry point, that is, the path to the file where the
|
/// The paths to the files where the application's [ReflectionCapabilities]
|
||||||
/// application's [ReflectionCapabilities] are set.
|
/// are set.
|
||||||
final String reflectionEntryPoint;
|
final List<String> reflectionEntryPoints;
|
||||||
|
|
||||||
/// The `BarbackMode#name` we are running in.
|
/// The `BarbackMode#name` we are running in.
|
||||||
final String modeName;
|
final String modeName;
|
||||||
|
|
||||||
TransformerOptions._internal(
|
TransformerOptions._internal(
|
||||||
this.entryPoint, this.reflectionEntryPoint, this.modeName);
|
this.entryPoints, this.reflectionEntryPoints, this.modeName);
|
||||||
|
|
||||||
factory TransformerOptions(String entryPoint,
|
factory TransformerOptions(List<String> entryPoints,
|
||||||
{String reflectionEntryPoint, String modeName: 'release'}) {
|
{List<String> reflectionEntryPoints, String modeName: 'release'}) {
|
||||||
if (entryPoint == null) {
|
if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) {
|
||||||
throw new ArgumentError.notNull(ENTRY_POINT_PARAM);
|
reflectionEntryPoints = entryPoints;
|
||||||
} else if (entryPoint.isEmpty) {
|
|
||||||
throw new ArgumentError.value(entryPoint, 'entryPoint');
|
|
||||||
}
|
|
||||||
if (reflectionEntryPoint == null || entryPoint.isEmpty) {
|
|
||||||
reflectionEntryPoint = entryPoint;
|
|
||||||
}
|
}
|
||||||
return new TransformerOptions._internal(
|
return new TransformerOptions._internal(
|
||||||
entryPoint, reflectionEntryPoint, modeName);
|
entryPoints, reflectionEntryPoints, modeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<String> _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;
|
||||||
|
}
|
|
@ -8,19 +8,29 @@ import 'directive_processor/transformer.dart';
|
||||||
import 'bind_generator/transformer.dart';
|
import 'bind_generator/transformer.dart';
|
||||||
import 'reflection_remover/transformer.dart';
|
import 'reflection_remover/transformer.dart';
|
||||||
import 'common/formatter.dart' as formatter;
|
import 'common/formatter.dart' as formatter;
|
||||||
|
import 'common/names.dart';
|
||||||
import 'common/options.dart';
|
import 'common/options.dart';
|
||||||
|
import 'common/options_reader.dart';
|
||||||
|
|
||||||
export 'common/options.dart';
|
export 'common/options.dart';
|
||||||
|
|
||||||
/// Removes the mirror-based initialization logic and replaces it with static
|
/// Removes the mirror-based initialization logic and replaces it with static
|
||||||
/// logic.
|
/// logic.
|
||||||
class DiTransformerGroup extends TransformerGroup {
|
class DiTransformerGroup extends TransformerGroup {
|
||||||
DiTransformerGroup()
|
DiTransformerGroup._(phases) : super(phases) {
|
||||||
: super([[new DirectiveProcessor(null)], [new DirectiveLinker()]]) {
|
|
||||||
formatter.init(new DartFormatter());
|
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) {
|
factory DiTransformerGroup.asPlugin(BarbackSettings settings) {
|
||||||
return new DiTransformerGroup();
|
return new DiTransformerGroup(parseBarbackSettings(settings));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,14 @@ class Codegen {
|
||||||
|
|
||||||
/// The prefix used to import our generated file.
|
/// The prefix used to import our generated file.
|
||||||
final String prefix;
|
final String prefix;
|
||||||
/// The import uri
|
/// The import uris
|
||||||
final String importUri;
|
final Iterable<String> importUris;
|
||||||
|
|
||||||
Codegen(String reflectionEntryPointPath, String newEntryPointPath,
|
Codegen(String reflectionEntryPointPath, Iterable<String> newEntryPointPaths,
|
||||||
{String prefix})
|
{String prefix})
|
||||||
: this.prefix = prefix == null ? _PREFIX_BASE : prefix,
|
: this.prefix = prefix == null ? _PREFIX_BASE : prefix,
|
||||||
importUri = path.relative(newEntryPointPath,
|
importUris = newEntryPointPaths.map((p) =>
|
||||||
from: path.dirname(reflectionEntryPointPath)) {
|
path.relative(p, from: path.dirname(reflectionEntryPointPath))) {
|
||||||
if (this.prefix.isEmpty) throw new ArgumentError.value('(empty)', 'prefix');
|
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
|
/// The code generated here should follow the example of code generated for
|
||||||
/// an [ImportDirective] node.
|
/// an [ImportDirective] node.
|
||||||
String codegenImport() {
|
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
|
/// Generates code to call the method which sets up Angular2 reflection
|
||||||
|
@ -63,7 +66,11 @@ class Codegen {
|
||||||
reflectorExpression = 'reflector';
|
reflectorExpression = 'reflector';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '${prefix}.${SETUP_METHOD_NAME}(${reflectorExpression});';
|
var count = 0;
|
||||||
|
return importUris
|
||||||
|
.map((_) =>
|
||||||
|
'${prefix}${count++}.${SETUP_METHOD_NAME}(${reflectorExpression});')
|
||||||
|
.join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
library angular2.transform.reflection_remover.remove_reflection_capabilities;
|
library angular2.transform.reflection_remover.remove_reflection_capabilities;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
import 'package:analyzer/analyzer.dart';
|
import 'package:analyzer/analyzer.dart';
|
||||||
|
import 'package:barback/barback.dart';
|
||||||
|
import 'package:angular2/src/transform/common/asset_reader.dart';
|
||||||
|
|
||||||
import 'codegen.dart';
|
import 'codegen.dart';
|
||||||
import 'rewriter.dart';
|
import 'rewriter.dart';
|
||||||
|
|
||||||
/// Finds the call to the Angular2 [ReflectionCapabilities] constructor
|
/// Finds the call to the Angular2 `ReflectionCapabilities` constructor
|
||||||
/// in [code] and replaces it with a call to `setupReflection` in
|
/// in [reflectionEntryPoint] and replaces it with a call to
|
||||||
/// [newEntryPoint].
|
/// `setupReflection` in [newEntryPoint].
|
||||||
///
|
///
|
||||||
/// [reflectionEntryPointPath] is the path where [code] is defined and is
|
/// This only searches the code in [reflectionEntryPoint], not `part`s,
|
||||||
/// used to display parsing errors.
|
/// `import`s, `export`s, etc.
|
||||||
///
|
Future<String> removeReflectionCapabilities(AssetReader reader,
|
||||||
/// This only searches [code] not `part`s, `import`s, `export`s, etc.
|
AssetId reflectionEntryPoint, Iterable<AssetId> newEntryPoints) async {
|
||||||
String removeReflectionCapabilities(
|
var code = await reader.readAsString(reflectionEntryPoint);
|
||||||
String code, String reflectionEntryPointPath, String newEntryPointPath) {
|
var reflectionEntryPointPath = reflectionEntryPoint.path;
|
||||||
var codegen = new Codegen(reflectionEntryPointPath, newEntryPointPath);
|
var newEntryPointPaths = newEntryPoints.map((id) => id.path);
|
||||||
|
|
||||||
|
var codegen = new Codegen(reflectionEntryPointPath, newEntryPointPaths);
|
||||||
return new Rewriter(code, codegen)
|
return new Rewriter(code, codegen)
|
||||||
.rewrite(parseCompilationUnit(code, name: reflectionEntryPointPath));
|
.rewrite(parseCompilationUnit(code, name: reflectionEntryPointPath));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
library angular2.transform.reflection_remover.transformer;
|
library angular2.transform.reflection_remover.transformer;
|
||||||
|
|
||||||
import 'dart:async';
|
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/logging.dart' as log;
|
||||||
import 'package:angular2/src/transform/common/names.dart';
|
import 'package:angular2/src/transform/common/names.dart';
|
||||||
import 'package:angular2/src/transform/common/options.dart';
|
import 'package:angular2/src/transform/common/options.dart';
|
||||||
|
@ -23,21 +24,24 @@ class ReflectionRemover extends Transformer {
|
||||||
ReflectionRemover(this.options);
|
ReflectionRemover(this.options);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isPrimary(AssetId id) => options.reflectionEntryPoint == id.path;
|
bool isPrimary(AssetId id) => options.reflectionEntryPoints != null &&
|
||||||
|
options.reflectionEntryPoints.contains(id.path);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future apply(Transform transform) async {
|
Future apply(Transform transform) async {
|
||||||
log.init(transform);
|
log.init(transform);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var newEntryPoint = new AssetId(
|
var newEntryPoints = options.entryPoints.map((entryPoint) {
|
||||||
transform.primaryInput.id.package, options.entryPoint)
|
return new AssetId(transform.primaryInput.id.package, entryPoint)
|
||||||
.changeExtension(DEPS_EXTENSION);
|
.changeExtension(DEPS_EXTENSION);
|
||||||
|
});
|
||||||
|
var reader = new AssetReader.fromTransform(transform);
|
||||||
|
|
||||||
var assetCode = await transform.primaryInput.readAsString();
|
var transformedCode = await removeReflectionCapabilities(
|
||||||
transform.addOutput(new Asset.fromString(transform.primaryInput.id,
|
reader, transform.primaryInput.id, newEntryPoints);
|
||||||
removeReflectionCapabilities(
|
transform.addOutput(
|
||||||
assetCode, transform.primaryInput.id.path, newEntryPoint.path)));
|
new Asset.fromString(transform.primaryInput.id, transformedCode));
|
||||||
} catch (ex, stackTrace) {
|
} catch (ex, stackTrace) {
|
||||||
log.logger.error('Removing reflection failed.\n'
|
log.logger.error('Removing reflection failed.\n'
|
||||||
'Exception: $ex\n'
|
'Exception: $ex\n'
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'template_compiler/transformer.dart';
|
||||||
import 'common/formatter.dart' as formatter;
|
import 'common/formatter.dart' as formatter;
|
||||||
import 'common/names.dart';
|
import 'common/names.dart';
|
||||||
import 'common/options.dart';
|
import 'common/options.dart';
|
||||||
|
import 'common/options_reader.dart';
|
||||||
|
|
||||||
export 'common/options.dart';
|
export 'common/options.dart';
|
||||||
|
|
||||||
|
@ -21,25 +22,17 @@ class AngularTransformerGroup extends TransformerGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AngularTransformerGroup(TransformerOptions options) {
|
factory AngularTransformerGroup(TransformerOptions options) {
|
||||||
var phases = [[new DirectiveProcessor(options)], [new DirectiveLinker()]];
|
var phases = [
|
||||||
if (options.modeName == TRANSFORM_MODE) {
|
[new ReflectionRemover(options)],
|
||||||
phases.addAll([
|
[new DirectiveProcessor(options)],
|
||||||
[new BindGenerator(options)],
|
[new DirectiveLinker()],
|
||||||
[new TemplateCompiler(options)],
|
[new BindGenerator(options)],
|
||||||
[new ReflectionRemover(options)]
|
[new TemplateCompiler(options)]
|
||||||
]);
|
];
|
||||||
}
|
|
||||||
return new AngularTransformerGroup._(phases);
|
return new AngularTransformerGroup._(phases);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AngularTransformerGroup.asPlugin(BarbackSettings settings) {
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ import '../common/read_file.dart';
|
||||||
|
|
||||||
var formatter = new DartFormatter();
|
var formatter = new DartFormatter();
|
||||||
var transform = new AngularTransformerGroup(new TransformerOptions(
|
var transform = new AngularTransformerGroup(new TransformerOptions(
|
||||||
'web/index.dart',
|
['web/index.dart'],
|
||||||
reflectionEntryPoint: 'web/index.dart', modeName: TRANSFORM_MODE));
|
reflectionEntryPoints: ['web/index.dart'], modeName: TRANSFORM_MODE));
|
||||||
|
|
||||||
class IntegrationTestConfig {
|
class IntegrationTestConfig {
|
||||||
final String name;
|
final String name;
|
||||||
|
|
|
@ -3,12 +3,10 @@ library web_foo.ng_deps.dart;
|
||||||
import 'index.dart';
|
import 'index.dart';
|
||||||
import 'package:angular2/src/core/application.dart';
|
import 'package:angular2/src/core/application.dart';
|
||||||
import 'package:angular2/src/reflection/reflection.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.dart';
|
||||||
import 'bar.ng_deps.dart' as i0;
|
import 'bar.ng_deps.dart' as i0;
|
||||||
import 'package:angular2/src/core/application.ng_deps.dart' as i1;
|
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;
|
bool _visited = false;
|
||||||
void initReflector(reflector) {
|
void initReflector(reflector) {
|
||||||
|
@ -16,5 +14,4 @@ void initReflector(reflector) {
|
||||||
_visited = true;
|
_visited = true;
|
||||||
i0.initReflector(reflector);
|
i0.initReflector(reflector);
|
||||||
i1.initReflector(reflector);
|
i1.initReflector(reflector);
|
||||||
i2.initReflector(reflector);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'reflection_remover_files/expected/index.dart' as expected;
|
||||||
import '../common/read_file.dart';
|
import '../common/read_file.dart';
|
||||||
|
|
||||||
void allTests() {
|
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.', () {
|
it('should remove uses of mirrors & insert calls to generated code.', () {
|
||||||
var code =
|
var code =
|
||||||
|
|
|
@ -13,10 +13,10 @@ library web_foo;
|
||||||
|
|
||||||
import 'package:angular2/src/core/application.dart';
|
import 'package:angular2/src/core/application.dart';
|
||||||
import 'package:angular2/src/reflection/reflection.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() {
|
void main() {
|
||||||
/*reflector.reflectionCapabilities = new ReflectionCapabilities();*/ngStaticInit.initReflector(reflector);
|
/*reflector.reflectionCapabilities = new ReflectionCapabilities();*/ngStaticInit0.initReflector(reflector);
|
||||||
bootstrap(MyComponent);
|
bootstrap(MyComponent);
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
|
@ -13,8 +13,8 @@ dev_dependencies:
|
||||||
path: ../benchpress
|
path: ../benchpress
|
||||||
transformers:
|
transformers:
|
||||||
- angular2:
|
- angular2:
|
||||||
entry_point: web/src/hello_world/index_common.dart
|
entry_points: web/src/hello_world/index_common.dart
|
||||||
reflection_entry_point: web/src/hello_world/index.dart
|
reflection_entry_points: web/src/hello_world/index.dart
|
||||||
- $dart2js:
|
- $dart2js:
|
||||||
minify: true
|
minify: true
|
||||||
commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]
|
commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]
|
||||||
|
|
Loading…
Reference in New Issue