1 | let Enumerable: any = require('linq');
|
2 | import {MetaUtils} from '../core/metadata/utils';
|
3 | import {Decorators} from '../core/constants';
|
4 | import {DecoratorType} from '../core/enums/decorator-type';
|
5 | import {MetaData} from '../core/metadata/metadata';
|
6 | import {ClassType, NodeModuleType} from '../core/utils/types';
|
7 | import {IInjectParams} from './decorators/interfaces/inject-params';
|
8 |
|
9 | import * as Utils from '../core/utils';
|
10 |
|
11 | let _extSources: Array<(any) => Object> = [];
|
12 | let _depInstMap = new Map();
|
13 | let _serviceMap = new Map<ClassType<any>, Object>();
|
14 |
|
15 | export function extSources(extSources?: Array<(any) => Object>) {
|
16 | if (extSources !== undefined) {
|
17 | _extSources = extSources;
|
18 | }
|
19 | return _extSources;
|
20 | }
|
21 |
|
22 | export function serviceMap(serviceMap?: Map<ClassType<any>, Object>) {
|
23 | if (serviceMap !== undefined) {
|
24 | _serviceMap = serviceMap;
|
25 | }
|
26 | return _serviceMap;
|
27 | }
|
28 |
|
29 |
|
30 |
|
31 | function generateToken(fn: Function) {
|
32 | return fn.toString();
|
33 | }
|
34 |
|
35 | class DependencyNode {
|
36 | parents: Map<ClassType<any>, boolean>;
|
37 | current: any;
|
38 | children: Map<ClassType<any>, boolean>;
|
39 | constructor(data) {
|
40 | this.parents = new Map<ClassType<any>, boolean>();
|
41 | this.children = new Map<ClassType<any>, boolean>();;
|
42 | this.current = data;
|
43 | }
|
44 | }
|
45 |
|
46 | let dependencyRoot: Map<ClassType<any>, DependencyNode> = new Map();
|
47 |
|
48 | let dependencyOrder: Map<ClassType<any>, number>;
|
49 |
|
50 | class DI {
|
51 | private stack: Array<{ parent: any; children: Array<any> }> = [];
|
52 |
|
53 | public resolveDependencies<T>(cls: ClassType<T>): T {
|
54 | if (_serviceMap.has(cls)) {
|
55 | return this.resolveServiceDependency<T>(cls, _serviceMap.get(cls));
|
56 | }
|
57 | return this.getFromExtSources<T>(cls);
|
58 | }
|
59 |
|
60 | private getFromExtSources<T>(cls: ClassType<any>): T {
|
61 | let inst;
|
62 | _extSources.forEach(func => {
|
63 | if (!inst) {
|
64 | inst = func.apply(this, [cls]);
|
65 | }
|
66 | });
|
67 | return inst;
|
68 | }
|
69 |
|
70 | private getDependencyOrderString(cls?: ClassType<any>) {
|
71 | let arr = [];
|
72 | dependencyOrder.forEach((value: number, key: ClassType<any>) => {
|
73 | arr.push(key.name);
|
74 | });
|
75 | cls && arr.push(cls.name);
|
76 | return arr.join('=>');
|
77 | }
|
78 |
|
79 | private resolveServiceDependency<T>(cls: ClassType<T>, service): T {
|
80 | let inst;
|
81 | if (!service.singleton) {
|
82 | inst = this.instantiateClass<T>(cls);
|
83 | }
|
84 | inst = this.getInstance(cls);
|
85 | if (inst) {
|
86 | return inst;
|
87 | } else {
|
88 | inst = this.instantiateClass<T>(cls);
|
89 | }
|
90 | return inst;
|
91 | }
|
92 |
|
93 | private getInstance<T>(cls: ClassType<T>): T {
|
94 | return _depInstMap.get(cls);
|
95 | }
|
96 |
|
97 | private getDependencies(cls: ClassType<any>): Array<MetaData> {
|
98 | return Enumerable.from(MetaUtils.getMetaData(cls.prototype, Decorators.INJECT));
|
99 | }
|
100 |
|
101 | private publicDeps(deps: Array<MetaData>): Array<MetaData> {
|
102 | return Enumerable.from(deps)
|
103 | .where((x: MetaData) => x.decoratorType === DecoratorType.PROPERTY)
|
104 | .toArray();
|
105 | }
|
106 |
|
107 | private constructorDeps(deps: Array<MetaData>): Array<MetaData> {
|
108 | return Enumerable.from(deps)
|
109 | .where((x: MetaData) => x.decoratorType === DecoratorType.PARAM)
|
110 | .toArray();
|
111 | }
|
112 |
|
113 | private resolveConstructorDeps(deps): Array<any> {
|
114 | let resolvedDeps = [];
|
115 | Enumerable.from(deps)
|
116 | .orderBy((x: MetaData) => x.paramIndex)
|
117 | .forEach((x: MetaData) => {
|
118 | let type = (<IInjectParams>x.params).type;
|
119 |
|
120 | if ((<any>type).default) {
|
121 | type = (<any>type).default;
|
122 | }
|
123 | if (!type) {
|
124 | console.log(x);
|
125 | throw 'no type found';
|
126 | }
|
127 | resolvedDeps.push(this.resolveDependencies(type));
|
128 | });
|
129 | return resolvedDeps;
|
130 | }
|
131 |
|
132 | private getType(params: any): ClassType<any> {
|
133 | let type = (<IInjectParams>params).type;
|
134 | if ((<any>type).__esModule) {
|
135 | type = (<any>type).default;
|
136 | }
|
137 | return type;
|
138 | }
|
139 |
|
140 | private resolvePropDeps(inst: any, propDeps: Array<any>) {
|
141 | Enumerable.from(propDeps)
|
142 | .forEach((x: MetaData) => {
|
143 | inst[x.propertyKey] = this.resolveDependencies((<IInjectParams>x.params).type);
|
144 | });
|
145 | }
|
146 |
|
147 |
|
148 | private getCycle<T>(parent: ClassType<T>, child: ClassType<T>) {
|
149 | let arr = Enumerable.from(this.stack)
|
150 | .select(x => x.parent.name)
|
151 | .toArray();
|
152 | arr.push(parent.name, child.name);
|
153 | return arr.join(" => ");
|
154 | }
|
155 |
|
156 |
|
157 | private instantiateClass<T>(cls: ClassType<T>): T {
|
158 | console.log('get dependencies: ' + cls.name);
|
159 |
|
160 | let allDependencies = this.getDependencies(cls);
|
161 | let deps = this.constructorDeps(allDependencies);
|
162 | let str = Enumerable.from(deps)
|
163 | .select((x: MetaData) => this.getType(x.params) ? this.getType(x.params).name : ' @ ')
|
164 | .toArray()
|
165 | .join(",");
|
166 | console.log(" " + str);
|
167 |
|
168 | if (!dependencyRoot.get(cls)) {
|
169 | dependencyRoot.set(cls, new DependencyNode(cls));
|
170 | }
|
171 |
|
172 | Enumerable.from(deps)
|
173 | .forEach((x: MetaData) => {
|
174 |
|
175 | let type = this.getType(x.params);
|
176 | if (!dependencyRoot.get(type)) {
|
177 | dependencyRoot.set(type, new DependencyNode(type));
|
178 | }
|
179 | dependencyRoot.get(cls).children.set(type, true);
|
180 |
|
181 | if (dependencyRoot.get(type).children.get(cls)) {
|
182 | let cycleDepStr = this.getCycle(cls, type);
|
183 | throw Error('Cycle found: ' + cycleDepStr);
|
184 | }
|
185 |
|
186 | dependencyRoot.get(type).parents.set(cls, true);
|
187 | });
|
188 |
|
189 | this.stack.push({ parent: cls, children: deps });
|
190 | let injectedProps = this.publicDeps(allDependencies);
|
191 |
|
192 | let resolvedDeps = this.resolveConstructorDeps(deps);
|
193 | let inst = Utils.activator<T>(cls, resolvedDeps);
|
194 | this.resolvePropDeps(inst, injectedProps);
|
195 |
|
196 | _depInstMap.set(cls, inst);
|
197 |
|
198 | this.stack.pop();
|
199 | return inst;
|
200 | }
|
201 | }
|
202 |
|
203 | export interface IContainer {
|
204 | addService<T>(cls: ClassType<T>, params: any);
|
205 | resolve<T>(cls: ClassType<T>): T;
|
206 | resolve<T>(module: NodeModuleType<T>): T;
|
207 | addSource(source: (any) => Object);
|
208 | }
|
209 |
|
210 | export class Container {
|
211 | |
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | public static addService<T>(cls: ClassType<T>, params: any) {
|
218 | _serviceMap.set(cls, params);
|
219 | }
|
220 |
|
221 | |
222 |
|
223 |
|
224 |
|
225 | public static resolve<T>(key: ClassType<T>|NodeModuleType<T>): T {
|
226 | let srvKey: ClassType<T> = <any>key;
|
227 | if ((<NodeModuleType<any>>key).__esModule) {
|
228 | srvKey = (<NodeModuleType<any>>key).default;
|
229 | }
|
230 | dependencyOrder = dependencyOrder || new Map<ClassType<any>, number>();
|
231 | let di = new DI();
|
232 | return di.resolveDependencies<T>(srvKey);
|
233 | }
|
234 |
|
235 | public static addSource(source: (any) => Object) {
|
236 | _extSources.push(source);
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | }
|
244 | } |
\ | No newline at end of file |