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:
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, '_');
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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 '';
|
||||
});
|
||||
}
|
||||
|
@ -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, '_');
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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});
|
||||
}
|
@ -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()));
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user