feat(transformers): collect provider information

This commit is contained in:
vsavkin 2016-03-02 11:17:02 -08:00 committed by Vikram Subramanian
parent 6402d61f69
commit 81beb1c788
7 changed files with 371 additions and 28 deletions

View File

@ -177,7 +177,10 @@ export class CompileProviderMetadata {
static fromJson(data: {[key: string]: any}): CompileProviderMetadata {
return new CompileProviderMetadata({
token: objFromJson(data['token'], CompileIdentifierMetadata.fromJson),
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson)
useClass: objFromJson(data['useClass'], CompileTypeMetadata.fromJson),
useExisting: objFromJson(data['useExisting'], CompileIdentifierMetadata.fromJson),
useValue: objFromJson(data['useValue'], CompileIdentifierMetadata.fromJson),
useFactory: objFromJson(data['useFactory'], CompileFactoryMetadata.fromJson)
});
}
@ -185,7 +188,10 @@ export class CompileProviderMetadata {
return {
// Note: Runtime type can't be serialized...
'token': objToJson(this.token),
'useClass': objToJson(this.useClass)
'useClass': objToJson(this.useClass),
'useExisting': objToJson(this.useExisting),
'useValue': objToJson(this.useValue),
'useFactory': objToJson(this.useFactory)
};
}
}
@ -198,15 +204,17 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata {
constConstructor: boolean;
diDeps: CompileDiDependencyMetadata[];
constructor({runtime, name, moduleUrl, constConstructor, diDeps}: {
constructor({runtime, name, moduleUrl, prefix, constConstructor, diDeps}: {
runtime?: Function,
name?: string,
prefix?: string,
moduleUrl?: string,
constConstructor?: boolean,
diDeps?: CompileDiDependencyMetadata[]
}) {
this.runtime = runtime;
this.name = name;
this.prefix = prefix;
this.moduleUrl = moduleUrl;
this.diDeps = diDeps;
this.constConstructor = constConstructor;
@ -214,7 +222,25 @@ export class CompileFactoryMetadata implements CompileIdentifierMetadata {
get identifier(): CompileIdentifierMetadata { return this; }
toJson() { return null; }
static fromJson(data: {[key: string]: any}): CompileFactoryMetadata {
return new CompileFactoryMetadata({
name: data['name'],
prefix: data['prefix'],
moduleUrl: data['moduleUrl'],
constConstructor: data['constConstructor'],
diDeps: arrayFromJson(data['diDeps'], CompileDiDependencyMetadata.fromJson)
});
}
toJson(): {[key: string]: any} {
return {
'name': this.name,
'prefix': this.prefix,
'moduleUrl': this.moduleUrl,
'constConstructor': this.constConstructor,
'diDeps': arrayToJson(this.diDeps)
};
}
}
/**

View File

@ -18,7 +18,9 @@ import {
CompileTemplateMetadata,
CompileProviderMetadata,
CompileDiDependencyMetadata,
CompileQueryMetadata
CompileQueryMetadata,
CompileIdentifierMetadata,
CompileFactoryMetadata
} from 'angular2/src/compiler/directive_metadata';
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection';
@ -31,25 +33,21 @@ export function main() {
var fullDirectiveMeta: CompileDirectiveMetadata;
beforeEach(() => {
fullTypeMeta = new CompileTypeMetadata({
name: 'SomeType',
moduleUrl: 'someUrl',
var diDep = new CompileDiDependencyMetadata({
isAttribute: true,
isSelf: true,
isHost: true,
diDeps: [
new CompileDiDependencyMetadata({
isAttribute: true,
isSelf: true,
isHost: true,
isSkipSelf: true,
isOptional: true,
token: 'someToken',
query: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
viewQuery: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
})
]
isSkipSelf: true,
isOptional: true,
token: 'someToken',
query: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'}),
viewQuery: new CompileQueryMetadata(
{selectors: ['one'], descendants: true, first: true, propertyName: 'one'})
});
fullTypeMeta = new CompileTypeMetadata(
{name: 'SomeType', moduleUrl: 'someUrl', isHost: true, diDeps: [diDep]});
fullTemplateMeta = new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.Emulated,
template: '<a></a>',
@ -69,7 +67,15 @@ export function main() {
outputs: ['someEvent'],
host: {'(event1)': 'handler1', '[prop1]': 'expr1', 'attr1': 'attrValue2'},
lifecycleHooks: [LifecycleHooks.OnChanges],
providers: [new CompileProviderMetadata({token: 'token', useClass: fullTypeMeta})]
providers: [
new CompileProviderMetadata({
token: 'token',
useClass: fullTypeMeta,
useExisting: new CompileIdentifierMetadata({name: 'someName'}),
useFactory: new CompileFactoryMetadata({name: 'someName', diDeps: [diDep]}),
useValue: 'someValue',
})
]
});
});

View File

@ -431,14 +431,38 @@ class _DirectiveMetadataVisitor extends Object
) {
final token = el.argumentList.arguments.first;
var useClass;
var useClass, useExisting, useValue, factoryId, useFactory, deps;
el.argumentList.arguments.skip(1).forEach((arg) {
if (arg.name.toString() == "useClass:") {
final id = _readIdentifier(arg.expression);
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
} else if (arg.name.toString() == "toClass:") {
final id = _readIdentifier(arg.expression);
useClass = new CompileTypeMetadata(prefix: id.prefix, name: id.name);
} else if (arg.name.toString() == "useExisting:") {
useExisting = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "toAlias:") {
useExisting = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "useValue:") {
useValue = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "toValue:") {
useValue = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "useFactory:") {
factoryId = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "toFactory:") {
factoryId = _readIdentifier(arg.expression);
} else if (arg.name.toString() == "deps:") {
deps = _readDeps(arg.expression);
}
});
return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass);
if (factoryId != null) {
useFactory = new CompileFactoryMetadata(name: factoryId.name, prefix: factoryId.prefix);
}
return new CompileProviderMetadata(token: _readIdentifier(token), useClass: useClass, useExisting: useExisting, useValue: useValue, useFactory: useFactory, deps: deps);
} else {
throw new ArgumentError(
@ -451,6 +475,29 @@ class _DirectiveMetadataVisitor extends Object
}
}
List<CompileDiDependencyMetadata> _readDeps(ListLiteral deps) {
return deps.elements.map((p) {
final list = p is ListLiteral ? p.elements : [p];
final first = list.first;
var token;
if (first is InstanceCreationExpression && first.constructorName.toString() == "Inject") {
token = _readIdentifier(first.argumentList.arguments[0]);
} else {
token = _readIdentifier(first);
}
return new CompileDiDependencyMetadata(
token: token,
isSelf: _hasConst(list, "Self"),
isHost: _hasConst(list, "Host"),
isSkipSelf: _hasConst(list, "SkipSelf"),
isOptional: _hasConst(list, "Optional"));
}).toList();
}
bool _hasConst(List list, String name) => list.where((m) => m is InstanceCreationExpression && m.constructorName.toString() == name).isNotEmpty;
//TODO Use AnnotationMatcher instead of string matching
bool _isAnnotation(Annotation node, String annotationName) {
var id = node.name;

View File

@ -162,6 +162,17 @@ class _CompileDataCreator {
if (provider.useClass != null) {
provider.useClass = _resolveIdentifier(ngMetaMap, neededBy, provider.useClass);
}
if (provider.useExisting != null) {
provider.useExisting = _resolveIdentifier(ngMetaMap, neededBy, provider.useExisting);
}
if (provider.useValue != null) {
provider.useValue = _resolveIdentifier(ngMetaMap, neededBy, provider.useValue);
}
if (provider.useFactory != null) {
final resolved = _resolveIdentifier(ngMetaMap, neededBy, provider.useFactory);
provider.useFactory.moduleUrl = resolved.moduleUrl;
_resolveDiDependencyMetadata(ngMetaMap, neededBy, provider.useFactory.diDeps);
}
resolvedProviders.add(provider);
}
}

View File

@ -671,6 +671,160 @@ void allTests() {
expect(useClass.name).toEqual("ServiceDep");
});
it('should populate `providers` using toClass.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToClass'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useExisting = cmp.providers.first.useClass;
expect(useExisting.prefix).toEqual(null);
expect(useExisting.name).toEqual("ServiceDep");
});
it('should populate `providers` using useExisting.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseExisting'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useExisting = cmp.providers.first.useExisting;
expect(useExisting.prefix).toEqual(null);
expect(useExisting.name).toEqual("ServiceDep");
});
it('should populate `providers` using toAlias.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToAlias'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useExisting = cmp.providers.first.useExisting;
expect(useExisting.prefix).toEqual(null);
expect(useExisting.name).toEqual("ServiceDep");
});
it('should populate `providers` using useExisting (string token).',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseExistingStr'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useExisting = cmp.providers.first.useExisting;
expect(useExisting).toEqual("StrToken");
});
it('should populate `providers` using useValue.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseValue'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useValue = cmp.providers.first.useValue;
expect(useValue.prefix).toEqual(null);
expect(useValue.name).toEqual("ServiceDep");
});
it('should populate `providers` using toValue.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToValue'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useValue = cmp.providers.first.useValue;
expect(useValue.prefix).toEqual(null);
expect(useValue.name).toEqual("ServiceDep");
});
it('should populate `providers` using useValue (string token).',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseValueStr'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useValue = cmp.providers.first.useValue;
expect(useValue).toEqual("StrToken");
});
it('should populate `providers` using useFactory.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersUseFactory'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useFactory = cmp.providers.first.useFactory;
var deps = cmp.providers.first.deps;
expect(useFactory.prefix).toEqual(null);
expect(useFactory.name).toEqual("funcDep");
expect(deps[0].token.name).toEqual("ServiceDep");
expect(deps[1].token).toEqual("Str");
expect(deps[2].token.name).toEqual("ServiceDep");
expect(deps[3].token.name).toEqual("ServiceDep");
expect(deps[3].isSelf).toEqual(true);
expect(deps[4].token.name).toEqual("ServiceDep");
expect(deps[4].isSkipSelf).toEqual(true);
expect(deps[5].token.name).toEqual("ServiceDep");
expect(deps[5].isOptional).toEqual(true);
});
it('should populate `providers` using toFactory.',
() async {
var cmp =
(await _testCreateModel('directives_files/components.dart')).identifiers['ComponentWithProvidersToFactory'];
expect(cmp).toBeNotNull();
expect(cmp.providers).toBeNotNull();
expect(cmp.providers.length).toEqual(1);
var token = cmp.providers.first.token;
var useFactory = cmp.providers.first.useFactory;
var deps = cmp.providers.first.deps;
expect(useFactory.prefix).toEqual(null);
expect(useFactory.name).toEqual("funcDep");
});
it('should populate `providers` using a string token.',
() async {
var cmp =

View File

@ -127,9 +127,82 @@ class ComponentWithDiDeps {
);
}
@Component(
selector: 'component-with-providers-use-class',
template: '',
providers: [const Provider(ServiceDep, useClass: ServiceDep)])
class ComponentWithProvidersUseClass {}
@Component(
selector: 'component-with-providers-to-class',
template: '',
providers: [const Binding(ServiceDep, toClass: ServiceDep)])
class ComponentWithProvidersToClass{}
@Component(
selector: 'component-with-providers-use-existing',
template: '',
providers: [const Provider(ServiceDep, useExisting: ServiceDep)])
class ComponentWithProvidersUseExisting{}
@Component(
selector: 'component-with-providers-to-alias',
template: '',
providers: [const Binding(ServiceDep, toAlias: ServiceDep)])
class ComponentWithProvidersToAlias{}
@Component(
selector: 'component-with-providers-use-existing-string',
template: '',
providers: [const Provider(ServiceDep, useExisting: 'StrToken')])
class ComponentWithProvidersUseExistingStr{}
@Component(
selector: 'component-with-providers-use-value',
template: '',
providers: [const Provider(ServiceDep, useValue: ServiceDep)])
class ComponentWithProvidersUseValue{}
@Component(
selector: 'component-with-providers-to-value',
template: '',
providers: [const Binding(ServiceDep, toValue: ServiceDep)])
class ComponentWithProvidersToValue{}
@Component(
selector: 'component-with-providers-use-value-string',
template: '',
providers: [const Provider(ServiceDep, useValue: 'StrToken')])
class ComponentWithProvidersUseValueStr{}
@Component(
selector: 'component-with-providers-use-factory',
template: '',
providers: [const Provider(ServiceDep, useFactory: funcDep, deps:
const [
ServiceDep,
"Str",
[const Inject(ServiceDep)],
[ServiceDep, const Self()],
[ServiceDep, const SkipSelf()],
[ServiceDep, const Optional()]
])])
class ComponentWithProvidersUseFactory{}
@Component(
selector: 'component-with-providers-to-factory',
template: '',
providers: [const Binding(ServiceDep, toFactory: funcDep)])
class ComponentWithProvidersToFactory{}
@Component(
selector: 'component-with-di-deps-string-token',
template: '')
class ComponentWithDiDepsStrToken {
ComponentWithDiDepsStrToken(@Inject("StringDep") arg1);
}
funcDep(){}

View File

@ -217,8 +217,17 @@ void allTests() {
barNgMeta.identifiers['Service2'] = new CompileTypeMetadata(name: 'Service2', moduleUrl: 'moduleUrl');
fooComponentMeta.template = new CompileTemplateMetadata(template: "import 'bar.dart';");
fooComponentMeta.providers = [new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useClass:
new CompileTypeMetadata(name: 'Service2'))];
fooComponentMeta.providers = [
new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useClass: new CompileTypeMetadata(name: 'Service2')),
new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useExisting: new CompileIdentifierMetadata(name: 'Service2')),
new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useValue: new CompileIdentifierMetadata(name: 'Service2')),
new CompileProviderMetadata(token: new CompileIdentifierMetadata(name: 'Service1'), useFactory:
new CompileFactoryMetadata(
name: 'Service2',
diDeps: [new CompileDiDependencyMetadata(token: new CompileIdentifierMetadata(name: 'Service2'))]
)
)
];
final viewAnnotation = new AnnotationModel()..name = 'View'..isView = true;
final reflectable = fooNgMeta.ngDeps.reflectables.first;
@ -230,12 +239,29 @@ void allTests() {
final viewDefResults = await createCompileData(reader, fooAssetId, [], [], {});
final cmp = viewDefResults.viewDefinitions.values.first.component;
expect(cmp.providers.length).toEqual(1);
expect(cmp.providers.length).toEqual(4);
expect(cmp.providers[0].token.name).toEqual("Service1");
expect(cmp.providers[0].token.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[0].useClass.name).toEqual("Service2");
expect(cmp.providers[0].useClass.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[1].token.name).toEqual("Service1");
expect(cmp.providers[1].token.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[1].useExisting.name).toEqual("Service2");
expect(cmp.providers[1].useExisting.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[2].token.name).toEqual("Service1");
expect(cmp.providers[2].token.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[2].useValue.name).toEqual("Service2");
expect(cmp.providers[2].useValue.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[3].token.name).toEqual("Service1");
expect(cmp.providers[3].token.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[3].useFactory.name).toEqual("Service2");
expect(cmp.providers[3].useFactory.moduleUrl).toEqual("moduleUrl");
expect(cmp.providers[3].useFactory.diDeps[0].token.name).toEqual("Service2");
expect(cmp.providers[3].useFactory.diDeps[0].token.moduleUrl).toEqual("moduleUrl");
});
it('should generate providers from Provider objects (literals).', () async {