feat(dart/transform): Fix handling of Dart keywords

Use `package:analyzer`'s list of Dart keywords to ensure we are properly
reporting usages of Dart keywords as runtime errors.
This commit is contained in:
Tim Blasi 2015-04-09 17:51:06 -07:00
parent 2cab7c79c3
commit f6e9d1f857
6 changed files with 75 additions and 14 deletions

View File

@ -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<String> 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<String> _generateSetters(Map<String, String> 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;
}

View File

@ -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.' ''';
}

View File

@ -200,6 +200,7 @@ class _Tester {
return metaName == 'Component' ||
metaName == 'Decorator' ||
metaName == 'Injectable' ||
metaName == 'View';
metaName == 'View' ||
metaName == 'Viewport';
}
}

View File

@ -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<String> processTemplates(AssetReader reader, AssetId entryPoint) async {
Iterable<String> _generateGetters(String typeName, List<String> 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<String> _generateSetters(String typeName, List<String> 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<String> _generateMethods(String typeName, List<String> 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

View File

@ -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});
}

View File

@ -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});
}