diff --git a/modules/angular2/src/transform/bind_generator/generator.dart b/modules/angular2/src/transform/bind_generator/generator.dart index f731256cab..14cdc2fd67 100644 --- a/modules/angular2/src/transform/bind_generator/generator.dart +++ b/modules/angular2/src/transform/bind_generator/generator.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/parser.dart'; +import 'package:angular2/src/transform/common/property_utils.dart' as prop; import 'package:barback/barback.dart'; import 'visitor.dart'; @@ -22,12 +23,20 @@ Future createNgSetters(AssetReader reader, AssetId entryPoint) async { '${code.substring(codeInjectIdx)}'; } +// TODO(kegluneq): De-dupe from template_compiler/generator.dart. + /// Consumes the map generated by [_createBindMap] to codegen setters. List _generateSetters(Map bindMap) { var setters = []; // TODO(kegluneq): Include types for receivers. See #886. - bindMap.forEach((prop, type) { - setters.add(''''$prop': (o, String v) => o.$prop = v'''); + bindMap.forEach((setterName, type) { + if (!prop.isValid(setterName)) { + // TODO(kegluenq): Eagerly throw here once #1295 is addressed. + setters.add(prop.lazyInvalidSetter(setterName)); + } else { + setters.add(''' '${prop.sanitize(setterName)}': ''' + ''' (o, v) => o.$setterName = v '''); + } }); return setters; } diff --git a/modules/angular2/src/transform/common/property_utils.dart b/modules/angular2/src/transform/common/property_utils.dart new file mode 100644 index 0000000000..835ffa39f4 --- /dev/null +++ b/modules/angular2/src/transform/common/property_utils.dart @@ -0,0 +1,33 @@ +library angular2.transform.common.property_utils; + +import 'package:analyzer/src/generated/scanner.dart' show Keyword; + +/// Whether `name` is a valid property name. +bool isValid(String name) => !Keyword.keywords.containsKey(name); + +/// Prepares [name] to be emitted inside a string. +String sanitize(String name) => name.replaceAll('\$', '\\\$'); + +/// Get a string usable as a lazy invalid setter, that is, one which will +/// `throw` immediately upon use. +String lazyInvalidSetter(String setterName) { + var sName = sanitize(setterName); + return ''' '$sName': (o, v) => ''' + ''' throw 'Invalid setter name "$sName" is a Dart keyword.' '''; +} + +/// Get a string usable as a lazy invalid getter, that is, one which will +/// `throw` immediately upon use. +String lazyInvalidGetter(String getterName) { + var sName = sanitize(getterName); + return ''' '$sName': (o) => ''' + ''' throw 'Invalid getter name "$sName" is a Dart keyword.' '''; +} + +/// Get a string usable as a lazy invalid method, that is, one which will +/// `throw` immediately upon use. +String lazyInvalidMethod(String methodName) { + var sName = sanitize(methodName); + return ''' '$sName': (o, args) => ''' + ''' throw 'Invalid method name "$sName" is a Dart keyword.' '''; +} diff --git a/modules/angular2/src/transform/directive_processor/rewriter.dart b/modules/angular2/src/transform/directive_processor/rewriter.dart index e188f42e7b..0a4ab9de9c 100644 --- a/modules/angular2/src/transform/directive_processor/rewriter.dart +++ b/modules/angular2/src/transform/directive_processor/rewriter.dart @@ -200,6 +200,7 @@ class _Tester { return metaName == 'Component' || metaName == 'Decorator' || metaName == 'Injectable' || - metaName == 'View'; + metaName == 'View' || + metaName == 'Viewport'; } } diff --git a/modules/angular2/src/transform/template_compiler/generator.dart b/modules/angular2/src/transform/template_compiler/generator.dart index 12c6ff4170..501a318886 100644 --- a/modules/angular2/src/transform/template_compiler/generator.dart +++ b/modules/angular2/src/transform/template_compiler/generator.dart @@ -16,6 +16,7 @@ import 'package:angular2/src/transform/common/asset_reader.dart'; import 'package:angular2/src/transform/common/logging.dart'; import 'package:angular2/src/transform/common/names.dart'; import 'package:angular2/src/transform/common/parser.dart'; +import 'package:angular2/src/transform/common/property_utils.dart' as prop; import 'package:barback/barback.dart'; import 'package:code_transformers/assets.dart'; @@ -63,21 +64,38 @@ Future processTemplates(AssetReader reader, AssetId entryPoint) async { Iterable _generateGetters(String typeName, List getterNames) { // TODO(kegluneq): Include `typeName` where possible. - return getterNames.map((prop) => ''' - '$prop': (o) => o.$prop - '''); + return getterNames.map((getterName) { + if (!prop.isValid(getterName)) { + // TODO(kegluenq): Eagerly throw here once #1295 is addressed. + return prop.lazyInvalidGetter(getterName); + } else { + return ''' '${prop.sanitize(getterName)}': (o) => o.$getterName'''; + } + }); } Iterable _generateSetters(String typeName, List setterName) { - return setterName.map((prop) => ''' - '$prop': (o, v) => o.$prop = v - '''); + return setterName.map((setterName) { + if (!prop.isValid(setterName)) { + // TODO(kegluenq): Eagerly throw here once #1295 is addressed. + return prop.lazyInvalidSetter(setterName); + } else { + return ''' '${prop.sanitize(setterName)}': ''' + ''' (o, v) => o.$setterName = v '''; + } + }); } Iterable _generateMethods(String typeName, List methodNames) { - return methodNames.map((methodName) => ''' - '$methodName': (o, List args) => Function.apply(o.$methodName, args) - '''); + return methodNames.map((methodName) { + if (!prop.isValid(methodName)) { + // TODO(kegluenq): Eagerly throw here once #1295 is addressed. + return prop.lazyInvalidMethod(methodName); + } else { + return ''' '${prop.sanitize(methodName)}': ''' + '(o, List args) => Function.apply(o.$methodName, args) '; + } + }); } /// Extracts `template` and `url` values from `View` annotations, reads diff --git a/modules/angular2/test/transform/bind_generator/basic_bind_files/expected/bar.ng_deps.dart b/modules/angular2/test/transform/bind_generator/basic_bind_files/expected/bar.ng_deps.dart index 2998dcf1c2..6a686a84b2 100644 --- a/modules/angular2/test/transform/bind_generator/basic_bind_files/expected/bar.ng_deps.dart +++ b/modules/angular2/test/transform/bind_generator/basic_bind_files/expected/bar.ng_deps.dart @@ -16,5 +16,5 @@ void initReflector(reflector) { selector: '[tool-tip]', properties: const {'text': 'tool-tip'}) ] }) - ..registerSetters({'text': (o, String v) => o.text = v}); + ..registerSetters({'text': (o, v) => o.text = v}); } diff --git a/modules/angular2/test/transform/bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart b/modules/angular2/test/transform/bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart index 473a4c4ded..1c0dc79d10 100644 --- a/modules/angular2/test/transform/bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart +++ b/modules/angular2/test/transform/bind_generator/duplicate_bind_name_files/expected/soup.ng_deps.dart @@ -22,5 +22,5 @@ void initReflector(reflector) { 'parameters': const [], 'annotations': const [const Component(properties: const {'menu': 'menu'})] }) - ..registerSetters({'menu': (o, String v) => o.menu = v}); + ..registerSetters({'menu': (o, v) => o.menu = v}); }