refactor(dart/transform): Generate `inputs` setters in `TemplateCompiler` step

Move generation of setters for `inputs` from `BindGenerator` into
`TemplateCompiler`.
This commit is contained in:
Tim Blasi 2015-10-07 10:03:50 -07:00
parent c94f239536
commit fcc6f2c561
14 changed files with 207 additions and 103 deletions

View File

@ -8,8 +8,6 @@ import 'package:angular2/src/transform/common/ng_deps.dart';
import 'package:angular2/src/transform/common/property_utils.dart' as prop;
import 'package:barback/barback.dart';
import 'visitor.dart';
class _ExtractQueryFieldsFromAnnotation extends Object
with RecursiveAstVisitor<Object> {
final ConstantEvaluator _evaluator = new ConstantEvaluator();
@ -73,7 +71,7 @@ Future<String> createNgSettersAndGetters(
NgDeps ngDeps = await NgDeps.parse(reader, entryPoint);
String code = ngDeps.code;
var setters = _generateSetters(_createInputPropertiesMap(ngDeps));
var setters = [];
ngDeps.registeredTypes.forEach((t) {
final fromAnnotation = new _ExtractQueryFieldsFromAnnotation();
@ -116,32 +114,3 @@ List<String> _generateSetters(Map<String, String> bindMap) {
});
return setters;
}
/// Collapses all `inputs` in {@link ngDeps} into a map where the keys are
/// the bind inputs and the values are either the one and only type
/// binding to that property or the empty string.
Map<String, String> _createInputPropertiesMap(NgDeps ngDeps) {
var visitor = new ExtractNamedExpressionVisitor('inputs');
var bindMap = {};
ngDeps.registeredTypes.forEach((RegisteredType t) {
visitor.bindConfig.clear();
t.annotations.accept(visitor);
visitor.bindConfig.forEach((String config) {
// See comments for `Directive` in annotations_impl/annotations.ts for
// details on how `inputs` is specified.
var prop;
var idx = config.indexOf(':');
if (idx > 0) {
prop = config.substring(0, idx).trim();
} else {
prop = config;
}
if (bindMap.containsKey(prop)) {
bindMap[prop] = '';
} else {
bindMap[prop] = '${t.typeName}';
}
});
});
return bindMap;
}

View File

@ -1,30 +0,0 @@
library angular2.transform.bind_generator.visitor;
import 'package:analyzer/analyzer.dart';
import 'package:angular2/src/transform/common/logging.dart';
/// Visitor responsible for crawling the "annotations" value in a
/// `registerType` call and pulling out the properties of any "bind"
/// values found.
class ExtractNamedExpressionVisitor extends Object
with RecursiveAstVisitor<Object> {
final ConstantEvaluator _evaluator = new ConstantEvaluator();
final List<String> bindConfig = [];
final String nameToExtract;
ExtractNamedExpressionVisitor(this.nameToExtract);
@override
Object visitNamedExpression(NamedExpression node) {
if ('${node.name.label}' == nameToExtract) {
var evaluated = node.expression.accept(_evaluator);
if (evaluated is List) {
bindConfig.addAll(evaluated);
} else {
logger.error('`$nameToExtract` currently only supports List values');
}
return null;
}
return super.visitNamedExpression(node);
}
}

View File

@ -15,8 +15,15 @@ class Processor implements CodegenModel {
final Set<ReflectiveAccessor> methodNames = new Set<ReflectiveAccessor>();
void process(CompileDirectiveMetadata meta) {
if (meta.outputs != null) {
meta.outputs.keys.forEach((eventName) {
getterNames.add(new ReflectiveAccessor(eventName));
});
}
if (meta.inputs != null) {
meta.inputs.keys.forEach((inputName) {
setterNames.add(new ReflectiveAccessor(inputName));
});
}
}
}

View File

@ -14,28 +14,6 @@ main() => allTests();
void allTests() {
var reader = new TestAssetReader();
it('should generate a setter for an `inputs` property in an annotation.',
() async {
var inputPath = 'basic_bind_files/bar.ng_deps.dart';
var expected = _readFile('basic_bind_files/expected/bar.ng_deps.dart');
var output = formatter
.format(await createNgSettersAndGetters(reader, _assetId(inputPath)));
expect(output).toEqual(expected);
});
it(
'should generate a single setter when multiple annotations bind to the '
'same `inputs` property.', () async {
var inputPath = 'duplicate_bind_name_files/soup.ng_deps.dart';
var expected =
_readFile('duplicate_bind_name_files/expected/soup.ng_deps.dart');
var output = formatter
.format(await createNgSettersAndGetters(reader, _assetId(inputPath)));
expect(output).toEqual(expected);
});
it('should generate setters for queries defined in the class annotation.',
() async {
var inputPath = 'queries_class_annotation_files/bar.ng_deps.dart';

View File

@ -159,7 +159,7 @@ void noChangeDetectorTests() {
_formatThenExpectEquals(output, expected);
});
it('should generate getters for Component#events.', () async {
it('should generate getters for Component#outputs.', () async {
var inputPath = 'template_compiler/event_files/hello.ng_deps.dart';
var expected =
readFile('template_compiler/event_files/expected/hello.ng_deps.dart');
@ -169,7 +169,7 @@ void noChangeDetectorTests() {
_formatThenExpectEquals(output, expected);
});
it('should generate getters for Directive#events.', () async {
it('should generate getters for Directive#outputs.', () async {
var inputPath =
'template_compiler/directive_event_files/hello.ng_deps.dart';
var expected = readFile(
@ -180,6 +180,39 @@ void noChangeDetectorTests() {
_formatThenExpectEquals(output, expected);
});
it('should generate setters for Component#inputs.', () async {
var inputPath = 'template_compiler/component_inputs_files/bar.ng_deps.dart';
var expected = readFile(
'template_compiler/component_inputs_files/expected/bar.ng_deps.dart');
var output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
});
it('should generate setters for Directive#inputs.', () async {
var inputPath = 'template_compiler/directive_inputs_files/bar.ng_deps.dart';
var expected = readFile(
'template_compiler/directive_inputs_files/expected/bar.ng_deps.dart');
var output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
output = await process(new AssetId('a', inputPath));
_formatThenExpectEquals(output, expected);
});
it(
'should generate a single setter for two `Directive`s '
'with the same inputs.', () async {
var inputPath =
'template_compiler/duplicate_input_name_files/soup.ng_deps.dart';
var expected = readFile(
'template_compiler/duplicate_input_name_files/expected/soup.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,18 @@
library bar.ng_deps.dart;
import 'bar.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ToolTip,
new ReflectionInfo(const [
const Component(
selector: '[tool-tip]', inputs: const ['text: tool-tip']),
const View(template: '<div>Tooltip</div>')
], const [], () => new ToolTip()));
}

View File

@ -0,0 +1,32 @@
{
"ToolTip":
{
"kind": "type",
"value": {
"isComponent": true,
"dynamicLoadable": true,
"selector":"[tool-tip]",
"exportAs": null,
"type": {
"id": 1,
"name": "ToolTip",
"moduleUrl": "asset:template_compiler/lib/basic_inputs_files/bar.dart"
},
"changeDetection": 5,
"inputs": {"text": "tool-tip"},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": {
"encapsulation": 0,
"template": "<div>Tooltip</div>",
"templateUrl": null,
"styles": null,
"styleUrls": null,
"ngContentSelectors": null
}
}
}
}

View File

@ -0,0 +1,22 @@
library bar.ng_deps.dart;
import 'bar.template.dart' as _templates;
import 'bar.dart';
import 'package:angular2/src/core/metadata.dart';
var _visited = false;
void initReflector(reflector) {
if (_visited) return;
_visited = true;
reflector
..registerType(
ToolTip,
new ReflectionInfo(const [
const Component(
selector: '[tool-tip]', inputs: const ['text: tool-tip']),
const View(template: '<div>Tooltip</div>'),
_templates.HostToolTipTemplate
], const [], () => new ToolTip()))
..registerSetters({'text': (o, v) => o.text = v});
}

View File

@ -0,0 +1,25 @@
{
"ToolTip":
{
"kind": "type",
"value": {
"isComponent": false,
"dynamicLoadable": true,
"selector":"[tool-tip]",
"exportAs": null,
"type": {
"id": 1,
"name": "ToolTip",
"moduleUrl": "asset:template_compiler/lib/basic_inputs_files/bar.dart"
},
"changeDetection": 5,
"inputs": {"text": "tool-tip"},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": null
}
}
}

View File

@ -9,16 +9,17 @@ void initReflector(reflector) {
_visited = true;
reflector
..registerType(
SoupComponent,
SoupDirective,
new ReflectionInfo(const [
const Component(
componentServices: const [SaladComponent],
const Directive(
selector: 'soup',
componentServices: const [SaladDirective],
inputs: const ['menu'])
], const [], () => new SoupComponent()))
], const [], () => new SoupDirective()))
..registerType(
SaladComponent,
SaladDirective,
new ReflectionInfo(const [
const Component(inputs: const ['menu'])
], const [], () => new SaladComponent()))
const Directive(selector: 'salad', inputs: const ['menu'])
], const [], () => new SaladDirective()))
..registerSetters({'menu': (o, v) => o.menu = v});
}

View File

@ -9,15 +9,16 @@ void initReflector(reflector) {
_visited = true;
reflector
..registerType(
SoupComponent,
SoupDirective,
new ReflectionInfo(const [
const Component(
componentServices: const [SaladComponent],
const Directive(
selector: 'soup',
componentServices: const [SaladDirective],
inputs: const ['menu'])
], const [], () => new SoupComponent()))
], const [], () => new SoupDirective()))
..registerType(
SaladComponent,
SaladDirective,
new ReflectionInfo(const [
const Component(inputs: const ['menu'])
], const [], () => new SaladComponent()));
const Directive(selector: 'salad', inputs: const ['menu'])
], const [], () => new SaladDirective()));
}

View File

@ -0,0 +1,48 @@
{
"SoupDirective":
{
"kind": "type",
"value": {
"isComponent": false,
"dynamicLoadable": true,
"selector":"soup",
"exportAs": null,
"type": {
"id": 1,
"name": "SoupDirective",
"moduleUrl": "asset:template_compiler/test/duplicate_input_name_files/soup.dart"
},
"changeDetection": 5,
"inputs": {"menu": "menu"},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": null
}
},
"SaladDirective":
{
"kind": "type",
"value": {
"isComponent": false,
"dynamicLoadable": true,
"selector":"salad",
"exportAs": null,
"type": {
"id": 1,
"name": "SaladDirective",
"moduleUrl": "asset:template_compiler/test/duplicate_input_name_files/soup.dart"
},
"changeDetection": 5,
"inputs": {"menu": "menu"},
"outputs": {},
"hostListeners": {},
"hostProperties": {},
"hostAttributes": {},
"lifecycleHooks": [],
"template": null
}
}
}