-
Notifications
You must be signed in to change notification settings - Fork 25.2k
/
injectable_compiler_2.ts
139 lines (125 loc) · 4.93 KB
/
injectable_compiler_2.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Identifiers} from './identifiers';
import * as o from './output/output_ast';
import {R3DependencyMetadata, R3FactoryDelegateType, R3FactoryMetadata, R3FactoryTarget, compileFactoryFunction} from './render3/r3_factory';
import {R3Reference, mapToMapExpression, typeWithParameters} from './render3/util';
export interface InjectableDef {
expression: o.Expression;
type: o.Type;
statements: o.Statement[];
}
export interface R3InjectableMetadata {
name: string;
type: R3Reference;
internalType: o.Expression;
typeArgumentCount: number;
providedIn: o.Expression;
useClass?: o.Expression;
useFactory?: o.Expression;
useExisting?: o.Expression;
useValue?: o.Expression;
userDeps?: R3DependencyMetadata[];
}
export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
let result: {factory: o.Expression, statements: o.Statement[]}|null = null;
const factoryMeta: R3FactoryMetadata = {
name: meta.name,
type: meta.type,
internalType: meta.internalType,
typeArgumentCount: meta.typeArgumentCount,
deps: [],
injectFn: Identifiers.inject,
target: R3FactoryTarget.Injectable,
};
if (meta.useClass !== undefined) {
// meta.useClass has two modes of operation. Either deps are specified, in which case `new` is
// used to instantiate the class with dependencies injected, or deps are not specified and
// the factory of the class is used to instantiate it.
//
// A special case exists for useClass: Type where Type is the injectable type itself and no
// deps are specified, in which case 'useClass' is effectively ignored.
const useClassOnSelf = meta.useClass.isEquivalent(meta.internalType);
let deps: R3DependencyMetadata[]|undefined = undefined;
if (meta.userDeps !== undefined) {
deps = meta.userDeps;
}
if (deps !== undefined) {
// factory: () => new meta.useClass(...deps)
result = compileFactoryFunction({
...factoryMeta,
delegate: meta.useClass,
delegateDeps: deps,
delegateType: R3FactoryDelegateType.Class,
});
} else if (useClassOnSelf) {
result = compileFactoryFunction(factoryMeta);
} else {
result = delegateToFactory(
meta.type.value as o.WrappedNodeExpr<any>, meta.useClass as o.WrappedNodeExpr<any>);
}
} else if (meta.useFactory !== undefined) {
if (meta.userDeps !== undefined) {
result = compileFactoryFunction({
...factoryMeta,
delegate: meta.useFactory,
delegateDeps: meta.userDeps || [],
delegateType: R3FactoryDelegateType.Function,
});
} else {
result = {
statements: [],
factory: o.fn([], [new o.ReturnStatement(meta.useFactory.callFn([]))])
};
}
} else if (meta.useValue !== undefined) {
// Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for
// client code because meta.useValue is an Expression which will be defined even if the actual
// value is undefined.
result = compileFactoryFunction({
...factoryMeta,
expression: meta.useValue,
});
} else if (meta.useExisting !== undefined) {
// useExisting is an `inject` call on the existing token.
result = compileFactoryFunction({
...factoryMeta,
expression: o.importExpr(Identifiers.inject).callFn([meta.useExisting]),
});
} else {
result = delegateToFactory(
meta.type.value as o.WrappedNodeExpr<any>, meta.internalType as o.WrappedNodeExpr<any>);
}
const token = meta.internalType;
const injectableProps: {[key: string]: o.Expression} = {token, factory: result.factory};
// Only generate providedIn property if it has a non-null value
if ((meta.providedIn as o.LiteralExpr).value !== null) {
injectableProps.providedIn = meta.providedIn;
}
const expression =
o.importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]);
const type = new o.ExpressionType(o.importExpr(
Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
return {
expression,
type,
statements: result.statements,
};
}
function delegateToFactory(type: o.WrappedNodeExpr<any>, internalType: o.WrappedNodeExpr<any>) {
return {
statements: [],
// If types are the same, we can generate `factory: type.ɵfac`
// If types are different, we have to generate a wrapper function to ensure
// the internal type has been resolved (`factory: function(t) { return type.ɵfac(t); }`)
factory: type.node === internalType.node ?
internalType.prop('ɵfac') :
o.fn([new o.FnParam('t', o.DYNAMIC_TYPE)], [new o.ReturnStatement(internalType.callMethod(
'ɵfac', [o.variable('t')]))])
};
}