UNPKG

6.38 kBJavaScriptView Raw
1"use strict";
2let Enumerable = require('linq');
3const utils_1 = require('../core/metadata/utils');
4const constants_1 = require('../core/constants');
5const decorator_type_1 = require('../core/enums/decorator-type');
6const Utils = require('../core/utils');
7let _extSources = [];
8let _depInstMap = new Map();
9let _serviceMap = new Map();
10function extSources(extSources) {
11 if (extSources !== undefined) {
12 _extSources = extSources;
13 }
14 return _extSources;
15}
16exports.extSources = extSources;
17function serviceMap(serviceMap) {
18 if (serviceMap !== undefined) {
19 _serviceMap = serviceMap;
20 }
21 return _serviceMap;
22}
23exports.serviceMap = serviceMap;
24//var services: Array<{ fn: Function, params: {} }> = [];
25//var serviceInstances: Array<{fn: Function, inst: any}> = [];
26function generateToken(fn) {
27 return fn.toString();
28}
29class DependencyNode {
30 constructor(data) {
31 this.parents = new Map();
32 this.children = new Map();
33 ;
34 this.current = data;
35 }
36}
37let dependencyRoot = new Map();
38let dependencyOrder;
39class DI {
40 constructor() {
41 this.stack = [];
42 }
43 resolveDependencies(cls) {
44 if (_serviceMap.has(cls)) {
45 return this.resolveServiceDependency(cls, _serviceMap.get(cls));
46 }
47 return this.getFromExtSources(cls);
48 }
49 getFromExtSources(cls) {
50 let inst;
51 _extSources.forEach(func => {
52 if (!inst) {
53 inst = func.apply(this, [cls]);
54 }
55 });
56 return inst;
57 }
58 getDependencyOrderString(cls) {
59 let arr = [];
60 dependencyOrder.forEach((value, key) => {
61 arr.push(key.name);
62 });
63 cls && arr.push(cls.name);
64 return arr.join('=>');
65 }
66 resolveServiceDependency(cls, service) {
67 let inst;
68 if (!service.singleton) {
69 inst = this.instantiateClass(cls);
70 }
71 inst = this.getInstance(cls);
72 if (inst) {
73 return inst;
74 }
75 else {
76 inst = this.instantiateClass(cls);
77 }
78 return inst;
79 }
80 getInstance(cls) {
81 return _depInstMap.get(cls);
82 }
83 getDependencies(cls) {
84 return Enumerable.from(utils_1.MetaUtils.getMetaData(cls.prototype, constants_1.Decorators.INJECT));
85 }
86 publicDeps(deps) {
87 return Enumerable.from(deps)
88 .where((x) => x.decoratorType === decorator_type_1.DecoratorType.PROPERTY)
89 .toArray();
90 }
91 constructorDeps(deps) {
92 return Enumerable.from(deps)
93 .where((x) => x.decoratorType === decorator_type_1.DecoratorType.PARAM)
94 .toArray();
95 }
96 resolveConstructorDeps(deps) {
97 let resolvedDeps = [];
98 Enumerable.from(deps)
99 .orderBy((x) => x.paramIndex)
100 .forEach((x) => {
101 let type = x.params.type;
102 if (type.default) {
103 type = type.default;
104 }
105 if (!type) {
106 console.log(x);
107 throw 'no type found';
108 }
109 resolvedDeps.push(this.resolveDependencies(type));
110 });
111 return resolvedDeps;
112 }
113 getType(params) {
114 let type = params.type;
115 if (type.__esModule) {
116 type = type.default;
117 }
118 return type;
119 }
120 resolvePropDeps(inst, propDeps) {
121 Enumerable.from(propDeps)
122 .forEach((x) => {
123 inst[x.propertyKey] = this.resolveDependencies(x.params.type);
124 });
125 }
126 //private instantiateClass<T extends Function>(fn: T): T {}
127 getCycle(parent, child) {
128 let arr = Enumerable.from(this.stack)
129 .select(x => x.parent.name)
130 .toArray();
131 arr.push(parent.name, child.name);
132 return arr.join(" => ");
133 }
134 // todo: cyclic dependency
135 instantiateClass(cls) {
136 console.log('get dependencies: ' + cls.name);
137 let allDependencies = this.getDependencies(cls);
138 let deps = this.constructorDeps(allDependencies);
139 let str = Enumerable.from(deps)
140 .select((x) => this.getType(x.params) ? this.getType(x.params).name : ' @ ')
141 .toArray()
142 .join(",");
143 console.log(" " + str);
144 if (!dependencyRoot.get(cls)) {
145 dependencyRoot.set(cls, new DependencyNode(cls));
146 }
147 Enumerable.from(deps)
148 .forEach((x) => {
149 let type = this.getType(x.params);
150 if (!dependencyRoot.get(type)) {
151 dependencyRoot.set(type, new DependencyNode(type));
152 }
153 dependencyRoot.get(cls).children.set(type, true);
154 if (dependencyRoot.get(type).children.get(cls)) {
155 let cycleDepStr = this.getCycle(cls, type);
156 throw Error('Cycle found: ' + cycleDepStr);
157 }
158 dependencyRoot.get(type).parents.set(cls, true);
159 });
160 this.stack.push({ parent: cls, children: deps });
161 let injectedProps = this.publicDeps(allDependencies);
162 let resolvedDeps = this.resolveConstructorDeps(deps);
163 let inst = Utils.activator(cls, resolvedDeps);
164 this.resolvePropDeps(inst, injectedProps);
165 _depInstMap.set(cls, inst);
166 this.stack.pop();
167 return inst;
168 }
169}
170class Container {
171 /**
172 * Registers all the services, i.e. classes having @service decorator, in the DI container.
173 * DI container will inject only those services that are registered with this method.
174 * @param {class} cls Typescript class having @service decorator
175 * @param {Object} params Decorator params
176 */
177 static addService(cls, params) {
178 _serviceMap.set(cls, params);
179 }
180 /**
181 *
182 * @param cls
183 */
184 static resolve(key) {
185 let srvKey = key;
186 if (key.__esModule) {
187 srvKey = key.default;
188 }
189 dependencyOrder = dependencyOrder || new Map();
190 let di = new DI();
191 return di.resolveDependencies(srvKey);
192 }
193 static addSource(source) {
194 _extSources.push(source);
195 //source.forEach((x, y) => {
196 // if (_depInstMap.has(y)) {
197 // throw 'key already present in the map';
198 // }
199 // _depInstMap.set(y, x);
200 //});
201 }
202}
203exports.Container = Container;
204
205//# sourceMappingURL=di.js.map