refactor(compiler): use the new compiler everywhere

Closes #3605

BREAKING CHANGE:
- we don't mark an element as bound any more if it only contains text bindings
  E.g. <div>{{hello}}</div>
  This changes the indices when using `DebugElement.componentViewChildren` / `DebugElement.children`.
- `@Directive.compileChildren` was removed,
  `ng-non-bindable` is now builtin and not a directive any more
- angular no more adds the `ng-binding` class to elements with bindings
- directives are now ordered as they are listed in the View.directives regarding change detection.
  Previously they had an undefined order.
- the `Renderer` interface has new methods `createProtoView` and `registerComponentTemplate`. See `DomRenderer` for default implementations.
- reprojection with `ng-content` is now all or nothing per `ng-content` element
- angular2 transformer can't be used in tests that modify directive metadata.
  Use `angular2/src/transform/inliner_for_test` transformer instead.
This commit is contained in:
Tobias Bosch
2015-10-01 10:07:49 -07:00
parent 30ca0434a2
commit 76247b7097
124 changed files with 2013 additions and 3451 deletions

View File

@ -1,19 +1,19 @@
library angular2.src.analysis.analyzer_plugin.src.tasks;
import 'package:analyzer/src/generated/ast.dart' hide Directive;
// import 'package:analyzer/src/generated/ast.dart' hide Directive;
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/task/general.dart';
import 'package:analyzer/task/dart.dart';
import 'package:analyzer/task/model.dart';
import 'package:angular2/src/core/render/api.dart';
import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
import 'package:angular2/src/compiler/directive_metadata.dart';
// import 'package:angular2/src/transform/common/directive_metadata_reader.dart';
/// The [RenderDirectiveMetadata]s of a [LibrarySpecificUnit].
final ListResultDescriptor<RenderDirectiveMetadata> DIRECTIVES =
new ListResultDescriptor<RenderDirectiveMetadata>('ANGULAR2_DIRECTIVES', null);
/// The [CompileDirectiveMetadata]s of a [LibrarySpecificUnit].
final ListResultDescriptor<CompileDirectiveMetadata> DIRECTIVES =
new ListResultDescriptor<CompileDirectiveMetadata>('ANGULAR2_DIRECTIVES', null);
/// A task that builds [RenderDirectiveMetadata]s for directive classes.
/// A task that builds [CompileDirectiveMetadata]s for directive classes.
class BuildUnitDirectivesTask extends SourceBasedAnalysisTask {
static const String UNIT_INPUT = 'UNIT_INPUT';
@ -29,17 +29,18 @@ class BuildUnitDirectivesTask extends SourceBasedAnalysisTask {
@override
void internalPerform() {
CompilationUnit unit = getRequiredInput(UNIT_INPUT);
// CompilationUnit unit = getRequiredInput(UNIT_INPUT);
List<RenderDirectiveMetadata> metaList = <RenderDirectiveMetadata>[];
for (CompilationUnitMember unitMember in unit.declarations) {
if (unitMember is ClassDeclaration) {
RenderDirectiveMetadata meta = readDirectiveMetadata(unitMember);
if (meta != null) {
metaList.add(meta);
}
}
}
List<CompileDirectiveMetadata> metaList = <CompileDirectiveMetadata>[];
// TODO(tbosch): figure our whether analyzer plugin is used at all...
// for (CompilationUnitMember unitMember in unit.declarations) {
// if (unitMember is ClassDeclaration) {
// CompileDirectiveMetadata meta = readDirectiveMetadata(unitMember);
// if (meta != null) {
// metaList.add(meta);
// }
// }
// }
outputs[DIRECTIVES] = metaList;
}

View File

@ -1,21 +1,16 @@
library angular2.transform.common.code.reflection_info_code;
library angular2.transform.common.code.source_module;
import 'package:angular2/src/compiler/source_module.dart';
import 'uri.dart';
/// Writes the full Dart code for the provided [SourceModule].
///
/// If `libraryName` is provided, the generated source will be generated with
/// the approprate "library" directive.
String writeSourceModule(SourceModule sourceModule, {String libraryName}) {
if (sourceModule == null) return null;
var buf = new StringBuffer();
var sourceWithImports = sourceModule.getSourceWithImports();
if (libraryName != null && libraryName.isNotEmpty) {
buf..writeln('library $libraryName;')..writeln();
}
var libraryName = _getLibName(sourceModule.moduleUrl);
buf..writeln('library $libraryName;')..writeln();
sourceWithImports.imports.forEach((import) {
// Format for importLine := [uri, prefix]
if (import.length != 2) {
@ -32,3 +27,13 @@ String writeSourceModule(SourceModule sourceModule, {String libraryName}) {
return buf.toString();
}
final _unsafeCharsPattern = new RegExp(r'[^a-zA-Z0-9_\.]');
String _getLibName(String moduleUrl) {
// TODO(tbosch): use `.replaceAll('/', '.')` here as well
// Also: replaceAll('asset:', '').
// Right now, this fails in some cases with Dart2Js, e.g.
// (Error 'switch' is a reserved word and can't be used here.
// library angular2_material.lib.src.components.switcher.switch.template.dart;)
return moduleUrl.replaceAll(_unsafeCharsPattern, '_');
}

View File

@ -1,4 +1,4 @@
library angular2.transform.common.code.reflection_info_code;
library angular2.transform.common.code.uri;
import 'package:angular2/src/transform/common/url_resolver.dart';
import 'package:path/path.dart' as path;

View File

@ -1,4 +1,4 @@
library angular2.transform.template_compiler.xhr_impl;
library angular2.transform.template_compiler.ng_compiler;
import 'package:angular2/src/compiler/command_compiler.dart';
import 'package:angular2/src/compiler/html_parser.dart';

View File

@ -8,7 +8,6 @@ import 'package:angular2/src/core/render/xhr.dart' show XHR;
import 'package:angular2/src/transform/common/asset_reader.dart';
import 'package:barback/barback.dart';
import 'common/options_reader.dart';
import 'common/asset_reader.dart';
import 'common/async_string_writer.dart';
import 'common/logging.dart';
@ -18,7 +17,9 @@ import 'directive_processor/inliner.dart';
/// Processes .dart files and inlines `templateUrl` and styleUrls` values.
class InlinerForTest extends Transformer implements DeclaringTransformer {
InlinerForTest();
final BarbackSettings settings;
InlinerForTest(this.settings);
@override
bool isPrimary(AssetId id) => id.extension.endsWith('dart');
@ -31,7 +32,7 @@ class InlinerForTest extends Transformer implements DeclaringTransformer {
@override
Future apply(Transform transform) async {
return log.initZoned(transform, () async {
return initZoned(transform, () async {
var primaryId = transform.primaryInput.id;
transform.consumePrimary();
var inlinedCode =
@ -45,7 +46,7 @@ class InlinerForTest extends Transformer implements DeclaringTransformer {
}
factory InlinerForTest.asPlugin(BarbackSettings settings) {
return new InlinerForTest(parseBarbackSettings(settings));
return new InlinerForTest(settings);
}
}
@ -132,7 +133,7 @@ class _ViewPropInliner extends ToSourceVisitor {
final resolvedUri = _urlResolver.resolve(_baseUri.toString(), url);
return _xhr.get(resolvedUri).catchError((_) {
logger.error('$_rootAssetId: could not read $url');
logger.error('$_baseUri: could not read $url');
return '';
});
}

View File

@ -24,14 +24,8 @@ Future<Iterable<Asset>> processStylesheet(
final sourceModules =
templateCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
var libraryIdx = 0;
return sourceModules.map((SourceModule module) => new Asset.fromString(
new AssetId.parse('${module.moduleUrl}'),
writeSourceModule(module,
libraryName: '${_getLibBase(module.moduleUrl)}${libraryIdx++}')));
writeSourceModule(module)));
}
final _unsafeCharsPattern = new RegExp(r'[^a-zA-Z0-9_]');
String _getLibBase(String libraryName) {
return libraryName.replaceAll('/', '.').replaceAll(_unsafeCharsPattern, '_');
}

View File

@ -29,8 +29,9 @@ class CompileDataResults {
final NgDeps ngDeps;
final Map<RegisteredType,
NormalizedComponentWithViewDirectives> viewDefinitions;
final List<CompileDirectiveMetadata> directiveMetadatas;
CompileDataResults._(this.ngDeps, this.viewDefinitions);
CompileDataResults._(this.ngDeps, this.viewDefinitions, this.directiveMetadatas);
}
// TODO(kegluenq): Improve this test.
@ -48,6 +49,7 @@ class _CompileDataCreator {
final AssetReader reader;
final AssetId entryPoint;
final Future<NgDeps> ngDepsFuture;
final List<CompileDirectiveMetadata> directiveMetadatas = [];
_CompileDataCreator(AssetReader reader, AssetId entryPoint)
: this.reader = reader,
@ -75,7 +77,7 @@ class _CompileDataCreator {
retVal[rType] = visitor.compileData;
}
});
return new CompileDataResults._(ngDeps, retVal);
return new CompileDataResults._(ngDeps, retVal, directiveMetadatas);
}
/// Creates a map from [AssetId] to import prefix for `.dart` libraries
@ -145,6 +147,9 @@ class _CompileDataCreator {
try {
var json = JSON.decode(await reader.readAsString(metaAssetId));
var newMetadata = new NgMeta.fromJson(json);
if (importAssetId == entryPoint) {
this.directiveMetadatas.addAll(newMetadata.types.values);
}
ngMeta.addAll(newMetadata);
} catch (ex, stackTrace) {
logger.warning('Failed to decode: $ex, $stackTrace',

View File

@ -29,7 +29,13 @@ import 'compile_data_creator.dart';
Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
{bool reflectPropertiesAsAttributes: false}) async {
var viewDefResults = await createCompileData(reader, entryPoint);
var codegen = null;
if (viewDefResults.directiveMetadatas.isNotEmpty) {
var processor = new reg.Processor();
viewDefResults.directiveMetadatas.forEach(processor.process);
codegen = new reg.Codegen();
codegen.generate(processor);
}
var templateCompiler = createTemplateCompiler(reader,
changeDetectionConfig: new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), reflectPropertiesAsAttributes, false));
@ -38,7 +44,7 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
var compileData =
viewDefResults.viewDefinitions.values.toList(growable: false);
if (compileData.isEmpty) {
return new Outputs(entryPoint, ngDeps, null, null, null);
return new Outputs(entryPoint, ngDeps, codegen, null, null);
}
var savedReflectionCapabilities = reflector.reflectionCapabilities;
@ -46,14 +52,6 @@ Future<Outputs> processTemplates(AssetReader reader, AssetId entryPoint,
var compiledTemplates = templateCompiler.compileTemplatesCodeGen(compileData);
reflector.reflectionCapabilities = savedReflectionCapabilities;
var processor = new reg.Processor();
compileData
.map((withDirectives) => withDirectives.component)
.forEach(processor.process);
var codegen = new reg.Codegen();
codegen.generate(processor);
return new Outputs(entryPoint, ngDeps, codegen,
viewDefResults.viewDefinitions, compiledTemplates);
}
@ -73,11 +71,9 @@ class Outputs {
reg.Codegen accessors,
Map<RegisteredType, NormalizedComponentWithViewDirectives> compileDataMap,
SourceModule templatesSource) {
var libraryName =
ngDeps != null && ngDeps.lib != null ? '${ngDeps.lib.name}' : null;
return new Outputs._(
_generateNgDepsCode(assetId, ngDeps, accessors, compileDataMap),
writeSourceModule(templatesSource, libraryName: libraryName));
writeSourceModule(templatesSource));
}
// Updates the NgDeps code with an additional `CompiledTemplate` annotation
@ -91,7 +87,7 @@ class Outputs {
Map<RegisteredType,
NormalizedComponentWithViewDirectives> compileDataMap) {
var code = ngDeps.code;
if (compileDataMap == null || compileDataMap.isEmpty) return code;
if (accessors == null && (compileDataMap == null || compileDataMap.isEmpty)) return code;
if (ngDeps.registeredTypes.isEmpty) return code;
var beginRegistrationsIdx =
@ -101,12 +97,16 @@ class Outputs {
// Add everything up to the point where we begin registering classes with
// the reflector, injecting an import to the generated template code.
var buf = new StringBuffer('${code.substring(0, importInjectIdx)}'
'import \'${toTemplateExtension(path.basename(id.path))}\' as _templates;'
'${code.substring(importInjectIdx, beginRegistrationsIdx)}');
var buf;
if (compileDataMap != null) {
buf = new StringBuffer('${code.substring(0, importInjectIdx)}'
'import \'${toTemplateExtension(path.basename(id.path))}\' as _templates;'
'${code.substring(importInjectIdx, beginRegistrationsIdx)}');
} else {
buf = new StringBuffer('${code.substring(0, beginRegistrationsIdx)}');
}
for (var registeredType in ngDeps.registeredTypes) {
if (compileDataMap.containsKey(registeredType)) {
if (compileDataMap != null && compileDataMap.containsKey(registeredType)) {
// We generated a template for this type, so add the generated
// `CompiledTemplate` value as the final annotation in the list.
var annotations = registeredType.annotations as ListLiteral;
@ -126,7 +126,6 @@ class Outputs {
registeredType.registerMethod.end));
}
}
buf.writeln(accessors.toString());
// Add everything after the final registration.

View File

@ -161,6 +161,17 @@ void noChangeDetectorTests() {
_formatThenExpectEquals(output, expected);
});
it('should generate getters for Directive#events.', () async {
var inputPath =
'template_compiler/directive_event_files/hello.ng_deps.dart';
var expected = readFile(
'template_compiler/directive_event_files/expected/hello.ng_deps.dart');
var output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
});
// TODO(kegluenq): Before committing, should this test be removed or just
// modified to check something different, maybe the created template code?
xit('should generate all expected getters, setters, & methods.', () async {

View File

@ -0,0 +1,20 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloDirective,
new ReflectionInfo(const [
const Directive(selector: 'hello', outputs: const ['eventName'])
], const [
const []
], () => new HelloDirective()))
..registerGetters({'eventName': (o) => o.eventName});
}

View File

@ -0,0 +1,19 @@
library examples.hello_world.index_common_dart.ng_deps.dart;
import 'hello.dart';
import 'package:angular2/angular2.dart'
show Component, Directive, View, NgElement;
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
HelloDirective,
new ReflectionInfo(const [
const Directive(selector: 'hello', outputs: const ['eventName'])
], const [
const []
], () => new HelloDirective()));
}

View File

@ -0,0 +1,32 @@
{
"HelloCmp":
{
"kind": "type",
"value": {
"isComponent": false,
"dynamicLoadable": true,
"selector":"hello-app",
"exportAs": null,
"type": {
"id": 1,
"name": "HelloCmp",
"moduleUrl": "asset:angular2/test/transform/template_compiler/directive_event_files/hello.dart"
},
"changeDetection": 5,
"properties": {},
"outputs": {"eventName": "eventName"},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "<button>go</button>",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}