UNPKG

11.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var Token_1 = require("./Token");
4var ServiceNotFoundError_1 = require("./error/ServiceNotFoundError");
5var MissingProvidedServiceTypeError_1 = require("./error/MissingProvidedServiceTypeError");
6var Container_1 = require("./Container");
7/**
8 * TypeDI can have multiple containers.
9 * One container is ContainerInstance.
10 */
11var ContainerInstance = /** @class */ (function () {
12 // -------------------------------------------------------------------------
13 // Constructor
14 // -------------------------------------------------------------------------
15 function ContainerInstance(id) {
16 // -------------------------------------------------------------------------
17 // Private Properties
18 // -------------------------------------------------------------------------
19 /**
20 * All registered services.
21 */
22 this.services = [];
23 this.id = id;
24 }
25 /**
26 * Checks if the service with given name or type is registered service container.
27 * Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
28 */
29 ContainerInstance.prototype.has = function (identifier) {
30 return !!this.findService(identifier);
31 };
32 /**
33 * Retrieves the service with given name or type from the service container.
34 * Optionally, parameters can be passed in case if instance is initialized in the container for the first time.
35 */
36 ContainerInstance.prototype.get = function (identifier) {
37 var globalContainer = Container_1.Container.of(undefined);
38 var service = globalContainer.findService(identifier);
39 var scopedService = this.findService(identifier);
40 if (service && service.global === true)
41 return this.getServiceValue(identifier, service);
42 if (scopedService)
43 return this.getServiceValue(identifier, scopedService);
44 if (service && this !== globalContainer) {
45 var clonedService = Object.assign({}, service);
46 clonedService.value = undefined;
47 var value = this.getServiceValue(identifier, clonedService);
48 this.set(identifier, value);
49 return value;
50 }
51 return this.getServiceValue(identifier, service);
52 };
53 /**
54 * Gets all instances registered in the container of the given service identifier.
55 * Used when service defined with multiple: true flag.
56 */
57 ContainerInstance.prototype.getMany = function (id) {
58 var _this = this;
59 return this.filterServices(id).map(function (service) { return _this.getServiceValue(id, service); });
60 };
61 /**
62 * Sets a value for the given type or service name in the container.
63 */
64 ContainerInstance.prototype.set = function (identifierOrServiceMetadata, value) {
65 var _this = this;
66 if (identifierOrServiceMetadata instanceof Array) {
67 identifierOrServiceMetadata.forEach(function (v) { return _this.set(v); });
68 return this;
69 }
70 if (typeof identifierOrServiceMetadata === "string" || identifierOrServiceMetadata instanceof Token_1.Token) {
71 return this.set({ id: identifierOrServiceMetadata, value: value });
72 }
73 if (identifierOrServiceMetadata instanceof Function) {
74 return this.set({ type: identifierOrServiceMetadata, id: identifierOrServiceMetadata, value: value });
75 }
76 // const newService: ServiceMetadata<any, any> = arguments.length === 1 && typeof identifierOrServiceMetadata === "object" && !(identifierOrServiceMetadata instanceof Token) ? identifierOrServiceMetadata : undefined;
77 var newService = identifierOrServiceMetadata;
78 var service = this.findService(newService.id);
79 if (service && service.multiple !== true) {
80 Object.assign(service, newService);
81 }
82 else {
83 this.services.push(newService);
84 }
85 return this;
86 };
87 /**
88 * Removes services with a given service identifiers (tokens or types).
89 */
90 ContainerInstance.prototype.remove = function () {
91 var _this = this;
92 var ids = [];
93 for (var _i = 0; _i < arguments.length; _i++) {
94 ids[_i] = arguments[_i];
95 }
96 ids.forEach(function (id) {
97 _this.filterServices(id).forEach(function (service) {
98 _this.services.splice(_this.services.indexOf(service), 1);
99 });
100 });
101 return this;
102 };
103 /**
104 * Completely resets the container by removing all previously registered services from it.
105 */
106 ContainerInstance.prototype.reset = function () {
107 this.services = [];
108 return this;
109 };
110 // -------------------------------------------------------------------------
111 // Private Methods
112 // -------------------------------------------------------------------------
113 /**
114 * Filters registered service in the with a given service identifier.
115 */
116 ContainerInstance.prototype.filterServices = function (identifier) {
117 return this.services.filter(function (service) {
118 if (service.id)
119 return service.id === identifier;
120 if (service.type && identifier instanceof Function)
121 return service.type === identifier || identifier.prototype instanceof service.type;
122 return false;
123 });
124 };
125 /**
126 * Finds registered service in the with a given service identifier.
127 */
128 ContainerInstance.prototype.findService = function (identifier) {
129 return this.services.find(function (service) {
130 if (service.id)
131 return service.id === identifier;
132 if (service.type && identifier instanceof Function)
133 return service.type === identifier; // todo: not sure why it was here || identifier.prototype instanceof service.type;
134 return false;
135 });
136 };
137 /**
138 * Gets service value.
139 */
140 ContainerInstance.prototype.getServiceValue = function (identifier, service) {
141 // find if instance of this object already initialized in the container and return it if it is
142 if (service && service.value !== null && service.value !== undefined)
143 return service.value;
144 // if named service was requested and its instance was not found plus there is not type to know what to initialize,
145 // this means service was not pre-registered and we throw an exception
146 if ((!service || !service.type) &&
147 (!service || !service.factory) &&
148 (typeof identifier === "string" || identifier instanceof Token_1.Token))
149 throw new ServiceNotFoundError_1.ServiceNotFoundError(identifier);
150 // at this point we either have type in service registered, either identifier is a target type
151 var type = undefined;
152 if (service && service.type) {
153 type = service.type;
154 }
155 else if (service && service.id instanceof Function) {
156 type = service.id;
157 }
158 else if (identifier instanceof Function) {
159 type = identifier;
160 }
161 // if service was not found then create a new one and register it
162 if (!service) {
163 if (!type)
164 throw new MissingProvidedServiceTypeError_1.MissingProvidedServiceTypeError(identifier);
165 service = { type: type };
166 this.services.push(service);
167 }
168 // setup constructor parameters for a newly initialized service
169 var paramTypes = type && Reflect && Reflect.getMetadata ? Reflect.getMetadata("design:paramtypes", type) : undefined;
170 var params = paramTypes ? this.initializeParams(type, paramTypes) : [];
171 // if factory is set then use it to create service instance
172 var value;
173 if (service.factory) {
174 // filter out non-service parameters from created service constructor
175 // non-service parameters can be, lets say Car(name: string, isNew: boolean, engine: Engine)
176 // where name and isNew are non-service parameters and engine is a service parameter
177 params = params.filter(function (param) { return param !== undefined; });
178 if (service.factory instanceof Array) {
179 // use special [Type, "create"] syntax to allow factory services
180 // in this case Type instance will be obtained from Container and its method "create" will be called
181 value = (_a = this.get(service.factory[0]))[service.factory[1]].apply(_a, params);
182 }
183 else {
184 value = service.factory.apply(service, params);
185 }
186 }
187 else {
188 if (!type)
189 throw new MissingProvidedServiceTypeError_1.MissingProvidedServiceTypeError(identifier);
190 params.unshift(null);
191 // "extra feature" - always pass container instance as the last argument to the service function
192 // this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
193 // need to be injected, and user can use provided container to get instances he needs
194 params.push(this);
195 value = new (type.bind.apply(type, params))();
196 }
197 if (service && !service.transient && value)
198 service.value = value;
199 if (type)
200 this.applyPropertyHandlers(type, value);
201 return value;
202 var _a;
203 };
204 /**
205 * Initializes all parameter types for a given target service class.
206 */
207 ContainerInstance.prototype.initializeParams = function (type, paramTypes) {
208 var _this = this;
209 return paramTypes.map(function (paramType, index) {
210 var paramHandler = Container_1.Container.handlers.find(function (handler) { return handler.object === type && handler.index === index; });
211 if (paramHandler)
212 return paramHandler.value(_this);
213 if (paramType && paramType.name && !_this.isTypePrimitive(paramType.name)) {
214 return _this.get(paramType);
215 }
216 return undefined;
217 });
218 };
219 /**
220 * Checks if given type is primitive (e.g. string, boolean, number, object).
221 */
222 ContainerInstance.prototype.isTypePrimitive = function (param) {
223 return ["string", "boolean", "number", "object"].indexOf(param.toLowerCase()) !== -1;
224 };
225 /**
226 * Applies all registered handlers on a given target class.
227 */
228 ContainerInstance.prototype.applyPropertyHandlers = function (target, instance) {
229 var _this = this;
230 Container_1.Container.handlers.forEach(function (handler) {
231 if (typeof handler.index === "number")
232 return;
233 if (handler.object.constructor !== target && !(target.prototype instanceof handler.object.constructor))
234 return;
235 instance[handler.propertyName] = handler.value(_this);
236 });
237 };
238 return ContainerInstance;
239}());
240exports.ContainerInstance = ContainerInstance;
241
242//# sourceMappingURL=ContainerInstance.js.map