##// END OF EJS Templates
working on IoC container
cin -
r38:d3813a6cdb36 di-typescript
parent child
Show More
@@ -1,4 +1,15
1 {
1 {
2 "java.configuration.updateBuildConfiguration": "disabled",
2 "java.configuration.updateBuildConfiguration": "disabled",
3 "tslint.enable": true
3 "tslint.enable": true,
4 "search.exclude": {
5 "**/node_modules": true,
6 "**/bower_components": true,
7 "/build": true
8 },
9 "files.watcherExclude": {
10 "**/.git/objects/**": true,
11 "**/.git/subtree-cache/**": true,
12 "**/node_modules/**": true,
13 "/build": true
14 }
4 } No newline at end of file
15 }
@@ -1,83 +1,83
1 import { IActivationController, IActivatable, ICancellation } from '../interfaces';
1 import { IActivationController, IActivatable, ICancellation } from "../interfaces";
2 import { AsyncComponent } from './AsyncComponent';
2 import { AsyncComponent } from "./AsyncComponent";
3 import { Cancellation } from '../Cancellation';
3 import { Cancellation } from "../Cancellation";
4 import { TraceSource } from '../log/TraceSource';
4 import { TraceSource } from "../log/TraceSource";
5
5
6 type Constructor<T = {}> = new (...args: any[]) => T;
6 type Constructor<T = {}> = new (...args: any[]) => T;
7
7
8 const log = TraceSource.get('@implab/core/components/ActivatableMixin');
8 const log = TraceSource.get("@implab/core/components/ActivatableMixin");
9
9
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
11 return class extends Base implements IActivatable {
11 return class extends Base implements IActivatable {
12 _controller: IActivationController;
12 _controller: IActivationController;
13
13
14 _active: boolean;
14 _active: boolean;
15
15
16 isActive() {
16 isActive() {
17 return this._active;
17 return this._active;
18 }
18 }
19
19
20 getActivationController() {
20 getActivationController() {
21 return this._controller;
21 return this._controller;
22 }
22 }
23
23
24 setActivationController(controller: IActivationController) {
24 setActivationController(controller: IActivationController) {
25 this._controller = controller;
25 this._controller = controller;
26 }
26 }
27
27
28 async onActivating(ct: ICancellation) {
28 async onActivating(ct: ICancellation) {
29 if (this._controller)
29 if (this._controller)
30 await this._controller.activating(this, ct);
30 await this._controller.activating(this, ct);
31 }
31 }
32
32
33 async onActivated(ct: ICancellation) {
33 async onActivated(ct: ICancellation) {
34 if (this._controller)
34 if (this._controller)
35 await this._controller.activated(this, ct);
35 await this._controller.activated(this, ct);
36 }
36 }
37
37
38 activate(ct: ICancellation = Cancellation.none) {
38 activate(ct: ICancellation = Cancellation.none) {
39 return this.runOperation(this._activateAsync.bind(this), ct);
39 return this.runOperation(this._activateAsync.bind(this), ct);
40 }
40 }
41
41
42 async _activateAsync(ct: ICancellation) {
42 async _activateAsync(ct: ICancellation) {
43 if (this.isActive())
43 if (this.isActive())
44 return;
44 return;
45
45
46 await this.onActivating(ct);
46 await this.onActivating(ct);
47 this._active = true;
47 this._active = true;
48 try {
48 try {
49 await this.onActivated(ct);
49 await this.onActivated(ct);
50 } catch (e) {
50 } catch (e) {
51 log.error("Suppressed onActivated error: {0}", e);
51 log.error("Suppressed onActivated error: {0}", e);
52 }
52 }
53 }
53 }
54
54
55 async onDeactivating(ct: ICancellation) {
55 async onDeactivating(ct: ICancellation) {
56 if (this._controller)
56 if (this._controller)
57 await this._controller.deactivating(this, ct);
57 await this._controller.deactivating(this, ct);
58 }
58 }
59
59
60 async onDeactivated(ct: ICancellation) {
60 async onDeactivated(ct: ICancellation) {
61 if (this._controller)
61 if (this._controller)
62 await this._controller.deactivated(this, ct);
62 await this._controller.deactivated(this, ct);
63 }
63 }
64
64
65 deactivate(ct: ICancellation = Cancellation.none) {
65 deactivate(ct: ICancellation = Cancellation.none) {
66 return this.runOperation(this._deactivateAsync.bind(this), ct);
66 return this.runOperation(this._deactivateAsync.bind(this), ct);
67 }
67 }
68
68
69 async _deactivateAsync(ct: ICancellation) {
69 async _deactivateAsync(ct: ICancellation) {
70 if (!this.isActive())
70 if (!this.isActive())
71 return;
71 return;
72 await this.onDeactivating(ct);
72 await this.onDeactivating(ct);
73 this._active = false;
73 this._active = false;
74 try {
74 try {
75 await this.onDeactivated(ct);
75 await this.onDeactivated(ct);
76 } catch (e) {
76 } catch (e) {
77 log.error("Suppressed onDeactivated error: {0}", e);
77 log.error("Suppressed onDeactivated error: {0}", e);
78 }
78 }
79 }
79 }
80 }
80 };
81 }
81 }
82
82
83 export const traceSource = log; No newline at end of file
83 export const traceSource = log;
@@ -1,24 +1,24
1 import { Descriptor } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3
3
4 export class AggregateDescriptor<T> implements Descriptor {
4 export class AggregateDescriptor<T> implements Descriptor {
5 _value: T
5 _value: T;
6
6
7 constructor(value: T) {
7 constructor(value: T) {
8
8
9 }
9 }
10
10
11 activate(context: ActivationContext, name: string) {
11 activate(context: ActivationContext, name: string) {
12 context.enter(name);
12 context.enter(name);
13 let v = context.parse(this._value, ".params");
13 const v = context.parse(this._value, ".params");
14 context.leave();
14 context.leave();
15 return v;
15 return v;
16 }
16 }
17
17
18 isInstanceCreated(): boolean {
18 isInstanceCreated(): boolean {
19 return false;
19 return false;
20 }
20 }
21 getInstance(): T {
21 getInstance(): T {
22 throw new Error("Not supported exception");
22 throw new Error("Not supported exception");
23 }
23 }
24 }
24 }
@@ -1,292 +1,263
1 declare function require(modules: string[], cb?: (...args: any[]) => any) : void;
2
3 declare function define(name:string, modules: string[], cb?: (...args: any[]) => any) : void;
4
5 import { Uuid } from "../Uuid";
6 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
7 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
8 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
9 import { isDescriptor, ActivationType } from "./interfaces";
4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration } from "./interfaces";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
5 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { isPrimitive } from "../safe";
6 import { isPrimitive } from "../safe";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
7 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { ServiceDescriptor } from "./ServiceDescriptor";
8 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
14
9 import { ModuleResolverBase } from "./ModuleResolverBase";
10 import format = require("../text/format");
15
11
16 export class Container {
12 export class Container {
17 _services
13 _services: ServiceMap;
18
14
19 _cache
15 _cache: object;
16
17 _cleanup: (() => void)[];
20
18
21 _cleanup: any[]
19 _root: Container;
22
20
23 _root: Container
21 _parent: Container;
24
22
25 _parent: Container
23 _resolver: ModuleResolverBase;
26
24
27 constructor(parent?: Container) {
25 constructor(parent?: Container) {
28 this._parent = parent;
26 this._parent = parent;
29 this._services = parent ? Object.create(parent._services) : {};
27 this._services = parent ? Object.create(parent._services) : {};
30 this._cache = {};
28 this._cache = {};
31 this._cleanup = [];
29 this._cleanup = [];
32 this._root = parent ? parent.getRootContainer() : this;
30 this._root = parent ? parent.getRootContainer() : this;
33 this._services.container = new ValueDescriptor(this);
31 this._services.container = new ValueDescriptor(this);
34 }
32 }
35
33
36 getRootContainer() {
34 getRootContainer() {
37 return this._root;
35 return this._root;
38 }
36 }
39
37
40 getParent() {
38 getParent() {
41 return this._parent;
39 return this._parent;
42 }
40 }
43
41
44 getService<T = any>(name: string, def?: T) {
42 getService<T = any>(name: string, def?: T) {
45 let d = this._services[name];
43 const d = this._services[name];
46 if (!d)
44 if (!d)
47 if (arguments.length > 1)
45 if (arguments.length > 1)
48 return def;
46 return def;
49 else
47 else
50 throw new Error("Service '" + name + "' isn't found");
48 throw new Error("Service '" + name + "' isn't found");
51
49
52 if (d.isInstanceCreated())
50 if (d.isInstanceCreated())
53 return d.getInstance();
51 return d.getInstance() as T;
54
52
55 var context = new ActivationContext(this, this._services);
53 const context = new ActivationContext(this, this._services);
56
54
57 try {
55 try {
58 return d.activate(context, name);
56 return d.activate(context, name) as T;
59 } catch (error) {
57 } catch (error) {
60 throw new ActivationError(name, context.getStack(), error);
58 throw new ActivationError(name, context.getStack(), error);
61 }
59 }
62 }
60 }
63
61
64 register(nameOrCollection, service?) {
62 register(nameOrCollection, service?) {
65 if (arguments.length == 1) {
63 if (arguments.length === 1) {
66 var data = nameOrCollection;
64 const data = nameOrCollection;
67 for (let name in data)
65 for (const name in data)
68 this.register(name, data[name]);
66 this.register(name, data[name]);
69 } else {
67 } else {
70 if (!(isDescriptor(service)))
68 if (!(isDescriptor(service)))
71 service = new ValueDescriptor(service);
69 service = new ValueDescriptor(service);
72 this._services[nameOrCollection] = service;
70 this._services[nameOrCollection] = service;
73 }
71 }
74 return this;
72 return this;
75 }
73 }
76
74
77 onDispose(callback) {
75 onDispose(callback) {
78 if (!(callback instanceof Function))
76 if (!(callback instanceof Function))
79 throw new Error("The callback must be a function");
77 throw new Error("The callback must be a function");
80 this._cleanup.push(callback);
78 this._cleanup.push(callback);
81 }
79 }
82
80
83 dispose() {
81 dispose() {
84 if (this._cleanup) {
82 if (this._cleanup) {
85 for (var i = 0; i < this._cleanup.length; i++)
83 for (const f of this._cleanup)
86 this._cleanup[i].call(null);
84 f();
87 this._cleanup = null;
85 this._cleanup = null;
88 }
86 }
89 }
87 }
90
88
91 /**
89 /**
92 * @param{String|Object} config
90 * @param{String|Object} config
93 * The configuration of the contaier. Can be either a string or an object,
91 * The configuration of the contaier. Can be either a string or an object,
94 * if the configuration is an object it's treated as a collection of
92 * if the configuration is an object it's treated as a collection of
95 * services which will be registed in the contaier.
93 * services which will be registed in the contaier.
96 *
94 *
97 * @param{Function} opts.contextRequire
95 * @param{Function} opts.contextRequire
98 * The function which will be used to load a configuration or types for services.
96 * The function which will be used to load a configuration or types for services.
99 *
97 *
100 */
98 */
101 async configure(config, opts) {
99 async configure(config: string | object, opts?: object) {
102 var me = this,
103 contextRequire = (opts && opts.contextRequire);
104
105 if (typeof (config) === "string") {
100 if (typeof (config) === "string") {
106 let args;
101 const resolver = await this._resolver.createResolver(config, opts);
107 if (!contextRequire) {
102 const data = await this._resolver.loadModule(config);
108 var shim = [config, Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
103 return this._configure(data, { resolver });
109 args = await new Promise((resolve, reject) => {
110 define(shim, ["require", config], function (ctx, data) {
111 resolve([data, {
112 contextRequire: ctx
113 }]);
114 })
115 });
116 require([shim]);
117 } else {
104 } else {
118 // TODO how to get correct contextRequire for the relative config module?
105 return this._configure(config);
119 args = await new Promise((resolve, reject) => {
120 contextRequire([config], function (data) {
121 resolve([data, {
122 contextRequire: contextRequire
123 }]);
124 });
125 });
126 }
127
128 return me._configure.apply(me, args);
129 } else {
130 return me._configure(config, opts);
131 }
106 }
132 }
107 }
133
108
134 createChildContainer() {
109 createChildContainer() {
135 return new Container(this);
110 return new Container(this);
136 }
111 }
137
112
138 has(id) {
113 has(id) {
139 return id in this._cache;
114 return id in this._cache;
140 }
115 }
141
116
142 get(id) {
117 get(id) {
143 return this._cache[id];
118 return this._cache[id];
144 }
119 }
145
120
146 store(id, value) {
121 store(id, value) {
147 return (this._cache[id] = value);
122 return (this._cache[id] = value);
148 }
123 }
149
124
150 async _configure(data, opts) {
125 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
151 var typemap = {},
126 const resolver = (opts && opts.resolver) || this._resolver;
152 me = this,
127
153 p,
128 const services: ServiceMap = {};
154 contextRequire = (opts && opts.contextRequire) || require;
155
129
156 var services = {};
130 resolver.beginBatch();
157
131
158 for (p in data) {
132 async function parse(k) {
159 var service = me._parse(data[p], typemap);
133 services[k] = await this._parse(data[k], resolver);
160 if (!(isDescriptor(service)))
161 service = new AggregateDescriptor(service);
162 services[p] = service;
163 }
134 }
164
135
165 me.register(services);
136 const batch = Object.keys(data).map(parse);
166
137
167 var names = [];
138 resolver.completeBatch();
168
169 for (p in typemap)
170 names.push(p);
171
139
172 return new Promise((resolve, reject) => {
140 await Promise.all(batch);
173 if (names.length) {
174 contextRequire(names, function () {
175 for (var i = 0; i < names.length; i++)
176 typemap[names[i]] = arguments[i];
177 resolve(me);
178 });
179 } else {
180 resolve(me);
181 }
182 });
183
141
142 this.register(services);
184 }
143 }
185
144
186 _parse(data, typemap) {
145 async _parse(registration: any, resolver: ModuleResolverBase) {
187 if (isPrimitive(data) || isDescriptor(data))
146 if (isPrimitive(registration) || isDescriptor(registration))
188 return data;
147 return registration;
189 if (data.$dependency) {
148
149 if (isDependencyRegistration(registration)) {
150
190 return new ReferenceDescriptor(
151 return new ReferenceDescriptor(
191 data.$dependency,
152 registration.$dependency,
192 data.lazy,
153 registration.lazy,
193 data.optional,
154 registration.optional,
194 data["default"],
155 registration["default"],
195 data.services && this._parseObject(data.services, typemap));
156 registration.services && this._parseObject(registration.services, resolver)
196 } else if (data.$value) {
157 );
197 return !data.parse ?
158
198 new ValueDescriptor(data.$value) :
159 } else if (isValueRegistration(registration)) {
199 new AggregateDescriptor(this._parse(data.$value, typemap));
160
200 } else if (data.$type || data.$factory) {
161 return !registration.parse ?
201 return this._parseService(data, typemap);
162 new ValueDescriptor(registration.$value) :
202 } else if (data instanceof Array) {
163 new AggregateDescriptor(this._parse(registration.$value, resolver));
203 return this._parseArray(data, typemap);
164
165 } else if (registration.$type || registration.$factory) {
166 return this._parseService(registration, resolver);
167 } else if (registration instanceof Array) {
168 return this._parseArray(registration, resolver);
204 }
169 }
205
170
206 return this._parseObject(data, typemap);
171 return this._parseObject(registration, resolver);
207 }
172 }
208
173
209 _parseService(data, typemap) {
174 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
210 var me = this,
175 const opts: ServiceDescriptorParams = {
211 opts: any = {
212 owner: this
176 owner: this
213 };
177 };
214 if (data.$type) {
215
178
216 opts.type = data.$type;
179 function guard<T>(fn: () => PromiseLike<T>) {
217
180 return fn();
218 if (typeof (data.$type) === "string") {
219 typemap[data.$type] = null;
220 opts.typeMap = typemap;
221 }
222 }
181 }
223
182
224 if (data.$factory)
183 if (data.$type) {
184 if (data.$type instanceof Function)
185 opts.type = data.$type;
186 else if (typeof data.$type === "string")
187 opts.type = await resolver.resolve(data.$type);
188 else
189 throw new Error(format("Unsupported type specification: {0:json}", data.$type));
190 } else {
191 if (data.$factory instanceof Function)
225 opts.factory = data.$factory;
192 opts.factory = data.$factory;
193 else if (typeof data.$factory === "string")
194 opts.factory = await resolver.resolve(data.$factory);
195 else
196 throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
197 }
226
198
227 if (data.services)
199 if (data.services)
228 opts.services = me._parseObject(data.services, typemap);
200 opts.services = await this._parseObject(data.services, resolver);
229 if (data.inject)
201
230 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
202 if (data.inject instanceof Array)
231 return me._parseObject(x, typemap);
203 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
232 }) : me._parseObject(data.inject, typemap);
204 else
205 opts.inject = this._parseObject(data.inject, resolver);
206
233 if (data.params)
207 if (data.params)
234 opts.params = me._parse(data.params, typemap);
208 opts.params = this._parse(data.params, resolver);
235
209
236 if (data.activation) {
210 if (data.activation) {
237 if (typeof (data.activation) === "string") {
211 if (typeof (data.activation) === "string") {
238 switch (data.activation.toLowerCase()) {
212 switch (data.activation.toLowerCase()) {
239 case "singleton":
213 case "singleton":
240 opts.activation = ActivationType.SINGLETON;
214 opts.activation = ActivationType.Singleton;
241 break;
215 break;
242 case "container":
216 case "container":
243 opts.activation = ActivationType.CONTAINER;
217 opts.activation = ActivationType.Container;
244 break;
218 break;
245 case "hierarchy":
219 case "hierarchy":
246 opts.activation = ActivationType.HIERARCHY;
220 opts.activation = ActivationType.Hierarchy;
247 break;
221 break;
248 case "context":
222 case "context":
249 opts.activation = ActivationType.CONTEXT;
223 opts.activation = ActivationType.Context;
250 break;
224 break;
251 case "call":
225 case "call":
252 opts.activation = ActivationType.CALL;
226 opts.activation = ActivationType.Call;
253 break;
227 break;
254 default:
228 default:
255 throw new Error("Unknown activation type: " +
229 throw new Error("Unknown activation type: " +
256 data.activation);
230 data.activation);
257 }
231 }
258 } else {
232 } else {
259 opts.activation = Number(data.activation);
233 opts.activation = Number(data.activation);
260 }
234 }
261 }
235 }
262
236
263 if (data.cleanup)
237 if (data.cleanup)
264 opts.cleanup = data.cleanup;
238 opts.cleanup = data.cleanup;
265
239
266 return new ServiceDescriptor(opts);
240 return new ServiceDescriptor(opts);
267 }
241 }
268
242
269 _parseObject(data, typemap) {
243 _parseObject(data: any, typemap) {
270 if (data.constructor &&
244 if (data.constructor &&
271 data.constructor.prototype !== Object.prototype)
245 data.constructor.prototype !== Object.prototype)
272 return new ValueDescriptor(data);
246 return new ValueDescriptor(data);
273
247
274 var o = {};
248 const o = {};
275
249
276 for (var p in data)
250 for (const p in data)
277 o[p] = this._parse(data[p], typemap);
251 o[p] = this._parse(data[p], typemap);
278
252
279 return o;
253 return o;
280 }
254 }
281
255
282 _parseArray(data, typemap) {
256 _parseArray(data, typemap) {
283 if (data.constructor &&
257 if (data.constructor &&
284 data.constructor.prototype !== Array.prototype)
258 data.constructor.prototype !== Array.prototype)
285 return new ValueDescriptor(data);
259 return new ValueDescriptor(data);
286
260
287 var me = this;
261 return data.map(x => this._parse(x, typemap));
288 return data.map(function (x) {
289 return me._parse(x, typemap);
290 });
291 }
262 }
292 } No newline at end of file
263 }
@@ -1,17 +1,22
1 import { argumentNotEmptyString, get } from "../safe";
1 import { argumentNotEmptyString, get } from "../safe";
2
2
3 export abstract class ModuleResolverBase {
3 export abstract class ModuleResolverBase {
4
4
5
6 async resolve(typeName: string) {
5 async resolve(typeName: string) {
7 argumentNotEmptyString(typeName, "typeName");
6 argumentNotEmptyString(typeName, "typeName");
8 let [moduleName, localName] = typeName.split("#", 2);
7 const [moduleName, localName] = typeName.split("#", 2);
9
8
10 let moduleObject = await this.loadModule(moduleName);
9 const moduleObject = await this.loadModule(moduleName);
11 return localName ? get(localName, moduleObject) : moduleObject;
10 return localName ? get(localName, moduleObject) : moduleObject;
12 }
11 }
13
12
14 abstract loadModule(moduleName: string): PromiseLike<Object>
13 beginBatch() {
14 }
15
15
16 abstract createResolver(moduleName: string): PromiseLike<ModuleResolverBase>
16 completeBatch() {
17 } No newline at end of file
17 }
18
19 abstract loadModule(moduleName: string): PromiseLike<object>;
20
21 abstract createResolver(moduleName: string, opts?: object): PromiseLike<ModuleResolverBase>;
22 }
@@ -1,75 +1,74
1 import { ModuleResolverBase } from "./ModuleResolverBase";
1 import { ModuleResolverBase } from "./ModuleResolverBase";
2 import { Uuid } from "../Uuid";
2 import { Uuid } from "../Uuid";
3 import { argumentNotEmptyString } from "../safe";
3 import { argumentNotEmptyString } from "../safe";
4 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
5
5
6 declare function require(modules: string[], cb?: (...args: any[]) => any): void;
6 declare function require(modules: string[], cb?: (...args: any[]) => any): void;
7
7
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
9
9
10 class RequireJsResolverParams {
10 interface RequireJsResolverParams {
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void;
12
12
13 base: string
13 base: string;
14 }
14 }
15
15
16 TraceSource.get("RequireJsResolver");
16 TraceSource.get("RequireJsResolver");
17
17
18 export class RequireJsResolver extends ModuleResolverBase {
18 export class RequireJsResolver extends ModuleResolverBase {
19 _contextRequire = require
19 _contextRequire = require;
20
20
21 _base: string
21 _base: string;
22
22
23 constructor(opts) {
23 constructor(opts) {
24 super();
24 super();
25
25
26 if (opts) {
26 if (opts) {
27
27
28 if (opts.contextRequire)
28 if (opts.contextRequire)
29 this._contextRequire = opts.contextRequire;
29 this._contextRequire = opts.contextRequire;
30
30
31 if (opts.base) {
31 if (opts.base) {
32 if (opts.base.indexOf("./") == 0)
32 if (opts.base.indexOf("./") === 0)
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
34 this._base = opts.base;
34 this._base = opts.base;
35 }
35 }
36 }
36 }
37
37
38 }
38 }
39
39
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
41 argumentNotEmptyString(moduleName, "moduleName");
41 argumentNotEmptyString(moduleName, "moduleName");
42
42
43 let parts = moduleName.split("/");
43 const parts = moduleName.split("/");
44 if (parts[0] == ".") {
44 if (parts[0] === ".") {
45 if (this._base)
45 if (this._base)
46 parts[0] = this._base;
46 parts[0] = this._base;
47 else
47 else
48 throw new Error(`Can't resolve a relative module '${moduleName}'`);
48 throw new Error(`Can't resolve a relative module '${moduleName}'`);
49 }
49 }
50
50
51 if(parts.length > 1)
51 if (parts.length > 1)
52 parts.splice(-1,1,Uuid());
52 parts.splice(-1, 1, Uuid());
53 else
53 else
54 parts.push(Uuid());
54 parts.push(Uuid());
55
55
56 var shim = parts.join('/');
56 const shim = parts.join("/");
57
57
58 let contextRequire = await new Promise((resolve, reject) => {
58 const contextRequire = await new Promise(
59 define(shim, ["require"], function (ctx) {
59 resolve => define(shim, ["require"], resolve)
60 resolve(ctx);
60 );
61 })
62 });
63
61
64 return new RequireJsResolver({
62 return new RequireJsResolver({
65 base: parts.slice(0,-1).join('/'),
63 base: parts.slice(0, -1).join("/"),
66 contextRequire: contextRequire
64 contextRequire
67 });
65 });
68 }
66 }
69
67
70 async loadModule(moduleName: string): Promise<Object> {
68 async loadModule(moduleName: string): Promise<object> {
71 return new Promise<Object>((resolve) => this._contextRequire.call(null, [moduleName], resolve)
69 return new Promise<object>(
70 resolve => this._contextRequire.call(null, [moduleName], resolve)
72 );
71 );
73 }
72 }
74
73
75 } No newline at end of file
74 }
@@ -1,275 +1,265
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
3 import { Container } from "./Container";
3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
5 import { ClientResponse } from "http";
5
6
6 let cacheId = 0;
7 let cacheId = 0;
7
8
8 function injectMethod(target, method, context, args) {
9 function injectMethod(target, method, context, args) {
9 var m = target[method];
10 const m = target[method];
10 if (!m)
11 if (!m)
11 throw new Error("Method '" + method + "' not found");
12 throw new Error("Method '" + method + "' not found");
12
13
13 if (args instanceof Array)
14 if (args instanceof Array)
14 m.apply(target, context.parse(args, "." + method));
15 m.apply(target, context.parse(args, "." + method));
15 else
16 else
16 m.call(target, context.parse(args, "." + method));
17 m.call(target, context.parse(args, "." + method));
17 };
18 }
18
19
19 function makeClenupCallback(target, method) {
20 function makeClenupCallback(target, method: (instance) => void | string) {
20 if (typeof (method) === "string") {
21 if (typeof (method) === "string") {
21 return function () {
22 return () => {
22 target[method]();
23 target[method]();
23 };
24 };
24 } else {
25 } else {
25 return function () {
26 return () => {
26 method(target);
27 method(target);
27 };
28 };
28 }
29 }
29 };
30 }
30
31
31 export interface ServiceDescriptorParams<T> {
32 export interface ServiceDescriptorParams<T = {}> {
32 activation: ActivationType
33 activation?: ActivationType;
33
34
34 owner: Container
35 owner: Container;
35
36
36 type: Constructor<T>
37 type?: Constructor<T>;
37
38
38 factory: Factory<T>
39 factory?: Factory<T>;
39
40
40 params
41 params?;
41
42
42 inject
43 inject?;
43
44
44 services: ServiceMap
45 services?: ServiceMap;
45
46
46 cleanup: (instance: T) => void
47 cleanup?: (instance: T) => void | string;
47 }
48 }
48
49
49 export class ServiceDescriptor<T> implements Descriptor {
50 export class ServiceDescriptor<T = {}> implements Descriptor {
50 _instance: T = null
51 _instance: T = null;
51
52
52 _hasInstance = false
53 _hasInstance = false;
53
54
54 _activationType = ActivationType.CALL
55 _activationType = ActivationType.Call;
55
56
56 _services: ServiceMap
57 _services: ServiceMap;
57
58
58 _type: Constructor<T> = null
59 _type: Constructor<T> = null;
59
60
60 _factory: Factory<T> = null
61 _factory: Factory<T> = null;
61
62
62 _params
63 _params;
63
64
64 _inject: Array<Object>
65 _inject: Array<object>;
65
66
66 _cleanup: (instance: T) => void
67 _cleanup: (instance: T) => void;
67
68
68 _cacheId: any
69 _cacheId: any;
69
70
70 _owner: Container
71 _owner: Container;
71
72
72 constructor(opts: ServiceDescriptorParams<T>) {
73 constructor(opts: ServiceDescriptorParams<T>) {
73 argumentNotNull(opts, "opts");
74 argumentNotNull(opts, "opts");
74 argumentNotNull(opts.owner, "owner");
75 argumentNotNull(opts.owner, "owner");
75
76
76 this._owner = opts.owner;
77 this._owner = opts.owner;
77
78
78 if (!(opts.type || opts.factory))
79 if (!(opts.type || opts.factory))
79 throw new Error(
80 throw new Error(
80 "Either a type or a factory must be specified");
81 "Either a type or a factory must be specified");
81
82
82 if (opts.activation)
83 if (opts.activation)
83 this._activationType = opts.activation;
84 this._activationType = opts.activation;
84
85
85 if (opts.type)
86 if (opts.type)
86 this._type = opts.type;
87 this._type = opts.type;
87
88
88 if (opts.params)
89 if (opts.params)
89 this._params = opts.params;
90 this._params = opts.params;
90
91
91 if (opts.inject)
92 if (opts.inject)
92 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
93 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
93
94
94 if (opts.services)
95 if (opts.services)
95 this._services = opts.services;
96 this._services = opts.services;
96
97
97 if (opts.factory)
98 if (opts.factory)
98 this._factory = opts.factory;
99 this._factory = opts.factory;
99
100
100 if (opts.cleanup) {
101 if (opts.cleanup) {
101 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
102 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
102 throw new Error(
103 throw new Error(
103 "The cleanup parameter must be either a function or a function name");
104 "The cleanup parameter must be either a function or a function name");
104
105
105 this._cleanup = opts.cleanup;
106 this._cleanup = opts.cleanup;
106 }
107 }
107
108
108 if (this._activationType == ActivationType.SINGLETON) {
109 if (this._activationType === ActivationType.Singleton) {
109 let tof = this._type || this._factory;
110 const tof = this._type || this._factory;
110
111
111 // create the persistent cache identifier for the type
112 // create the persistent cache identifier for the type
112 if (isPrimitive(tof))
113 if (isPrimitive(tof))
113 this._cacheId = tof;
114 this._cacheId = tof;
114 else
115 else
115 this._cacheId = oid(tof);
116 this._cacheId = oid(tof);
116 } else {
117 } else {
117 this._cacheId = ++cacheId;
118 this._cacheId = ++cacheId;
118 }
119 }
119 }
120 }
120
121
121 activate(context: ActivationContext, name: string) {
122 activate(context: ActivationContext, name: string) {
122 // if we have a local service records, register them first
123 // if we have a local service records, register them first
123 let instance;
124 let instance;
124
125
125 switch (this._activationType) {
126 switch (this._activationType) {
126 case ActivationType.SINGLETON: // SINGLETON
127 case ActivationType.Singleton: // SINGLETON
127 // if the value is cached return it
128 // if the value is cached return it
128 if (this._hasInstance)
129 if (this._hasInstance)
129 return this._instance;
130 return this._instance;
130
131
131 // singletons are bound to the root container
132 // singletons are bound to the root container
132 let container = context.container.getRootContainer();
133 const container = context.container.getRootContainer();
133
134
134 if (container.has(this._cacheId)) {
135 if (container.has(this._cacheId)) {
135 instance = container.get(this._cacheId);
136 instance = container.get(this._cacheId);
136 } else {
137 } else {
137 instance = this._create(context, name);
138 instance = this._create(context, name);
138 container.store(this._cacheId, instance);
139 container.store(this._cacheId, instance);
139 if (this._cleanup)
140 if (this._cleanup)
140 container.onDispose(
141 container.onDispose(
141 makeClenupCallback(instance, this._cleanup));
142 makeClenupCallback(instance, this._cleanup));
142 }
143 }
143
144
144 this._hasInstance = true;
145 this._hasInstance = true;
145 return (this._instance = instance);
146 return (this._instance = instance);
146
147
147 case ActivationType.CONTAINER: // CONTAINER
148 case ActivationType.Container: // CONTAINER
148 //return a cached value
149 // return a cached value
150
149 if (this._hasInstance)
151 if (this._hasInstance)
150 return this._instance;
152 return this._instance;
151
153
152 // create an instance
154 // create an instance
153 instance = this._create(context, name);
155 instance = this._create(context, name);
154
156
155 // the instance is bound to the container
157 // the instance is bound to the container
156 if (this._cleanup)
158 if (this._cleanup)
157 this._owner.onDispose(
159 this._owner.onDispose(
158 makeClenupCallback(instance, this._cleanup));
160 makeClenupCallback(instance, this._cleanup));
159
161
160 // cache and return the instance
162 // cache and return the instance
161 this._hasInstance = true;
163 this._hasInstance = true;
162 return (this._instance = instance);
164 return (this._instance = instance);
163 case ActivationType.CONTEXT: // CONTEXT
165 case ActivationType.Context: // CONTEXT
164 //return a cached value if one exists
166 // return a cached value if one exists
167
165 if (context.has(this._cacheId))
168 if (context.has(this._cacheId))
166 return context.get(this._cacheId);
169 return context.get(this._cacheId);
167 // context context activated instances are controlled by callers
170 // context context activated instances are controlled by callers
168 return context.store(this._cacheId, this._create(
171 return context.store(this._cacheId, this._create(
169 context,
172 context,
170 name));
173 name));
171 case ActivationType.CALL: // CALL
174 case ActivationType.Call: // CALL
172 // per-call created instances are controlled by callers
175 // per-call created instances are controlled by callers
173 return this._create(context, name);
176 return this._create(context, name);
174 case ActivationType.HIERARCHY: // HIERARCHY
177 case ActivationType.Hierarchy: // HIERARCHY
175 // hierarchy activated instances are behave much like container activated
178 // hierarchy activated instances are behave much like container activated
176 // except they are created and bound to the child container
179 // except they are created and bound to the child container
177
180
178 // return a cached value
181 // return a cached value
179 if (context.container.has(this._cacheId))
182 if (context.container.has(this._cacheId))
180 return context.container.get(this._cacheId);
183 return context.container.get(this._cacheId);
181
184
182 instance = this._create(context, name);
185 instance = this._create(context, name);
183
186
184 if (this._cleanup)
187 if (this._cleanup)
185 context.container.onDispose(makeClenupCallback(
188 context.container.onDispose(makeClenupCallback(
186 instance,
189 instance,
187 this._cleanup));
190 this._cleanup));
188
191
189 return context.container.store(this._cacheId, instance);
192 return context.container.store(this._cacheId, instance);
190 default:
193 default:
191 throw "Invalid activation type: " + this._activationType;
194 throw new Error("Invalid activation type: " + this._activationType);
192 }
195 }
193 }
196 }
194
197
195 isInstanceCreated() {
198 isInstanceCreated() {
196 return this._hasInstance;
199 return this._hasInstance;
197 }
200 }
198
201
199 getInstance() {
202 getInstance() {
200 return this._instance;
203 return this._instance;
201 }
204 }
202
205
203 _create(context, name) {
206 _create(context, name) {
204 context.enter(name, this, Boolean(this._services));
207 context.enter(name, this, Boolean(this._services));
205
208
206 if (this._activationType != ActivationType.CALL &&
209 if (this._activationType !== ActivationType.Call &&
207 context.visit(this._cacheId) > 0)
210 context.visit(this._cacheId) > 0)
208 throw new Error("Recursion detected");
211 throw new Error("Recursion detected");
209
212
210 if (this._services) {
213 if (this._services) {
211 for (var p in this._services)
214 for (const p in this._services)
212 context.register(p, this._services[p]);
215 context.register(p, this._services[p]);
213 }
216 }
214
217
215 var instance;
218 let instance;
216
219
217 if (!this._factory) {
220 if (!this._factory) {
218 var ctor = this._type;
221 const ctor = this._type;
219
222 this._factory = (...args) => {
220 if (this._params === undefined) {
223 return new ctor(...args);
221 this._factory = function () {
222 return new ctor();
223 };
224 };
224 } else if (this._params instanceof Array) {
225 this._factory = function () {
226 var inst = Object.create(ctor.prototype);
227 var ret = ctor.apply(inst, arguments);
228 return typeof (ret) === "object" ? ret : inst;
229 };
230 } else {
231 this._factory = function (param) {
232 return new ctor(param);
233 };
234 }
235 }
225 }
236
226
237 if (this._params === undefined) {
227 if (this._params === undefined) {
238 instance = this._factory();
228 instance = this._factory();
239 } else if (this._params instanceof Array) {
229 } else if (this._params instanceof Array) {
240 instance = this._factory.apply(this, context.parse(
230 instance = this._factory.apply(this, context.parse(
241 this._params,
231 this._params,
242 ".params"));
232 ".params"));
243 } else {
233 } else {
244 instance = this._factory(context.parse(
234 instance = this._factory(context.parse(
245 this._params,
235 this._params,
246 ".params"));
236 ".params"));
247 }
237 }
248
238
249 if (this._inject) {
239 if (this._inject) {
250 this._inject.forEach(function (spec) {
240 this._inject.forEach(spec => {
251 for (var m in spec)
241 for (const m in spec)
252 injectMethod(instance, m, context, spec[m]);
242 injectMethod(instance, m, context, spec[m]);
253 });
243 });
254 }
244 }
255
245
256 context.leave();
246 context.leave();
257
247
258 return instance;
248 return instance;
259 }
249 }
260
250
261 // @constructor {singleton} foo/bar/Baz
251 // @constructor {singleton} foo/bar/Baz
262 // @factory {singleton}
252 // @factory {singleton}
263 toString() {
253 toString() {
264 var parts = [];
254 const parts = [];
265
255
266 parts.push(this._type ? "@constructor" : "@factory");
256 parts.push(this._type ? "@constructor" : "@factory");
267
257
268 parts.push(ActivationType[this._activationType]);
258 parts.push(ActivationType[this._activationType]);
269
259
270 if (typeof (this._type) === "string")
260 if (typeof (this._type) === "string")
271 parts.push(this._type);
261 parts.push(this._type);
272
262
273 return parts.join(" ");
263 return parts.join(" ");
274 }
264 }
275 } No newline at end of file
265 }
@@ -1,28 +1,71
1 import { isNull } from "../safe";
1 import { isNull } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3
3
4 export interface Descriptor {
4 export interface Descriptor {
5 activate(context: ActivationContext, name?: string);
5 activate(context: ActivationContext, name?: string);
6 isInstanceCreated(): boolean;
7 getInstance();
6 }
8 }
7
9
8 export type Constructor<T = {}> = new (...args: any[]) => T;
10 export type Constructor<T = {}> = new (...args: any[]) => T;
9
11
10
11 export type Factory<T = {}> = (...args: any[]) => T;
12 export type Factory<T = {}> = (...args: any[]) => T;
12
13
13 export function isDescriptor(instance): instance is Descriptor {
14 export function isDescriptor(instance): instance is Descriptor {
14 return (!isNull(instance)) &&
15 return (!isNull(instance)) &&
15 ('activate' in instance);
16 ("activate" in instance);
16 }
17 }
17
18
18 export interface ServiceMap {
19 export interface ServiceMap {
19 [s: string] : Descriptor
20 [s: string]: Descriptor;
20 }
21 }
21
22
22 export enum ActivationType {
23 export enum ActivationType {
23 SINGLETON,
24 Singleton,
24 CONTAINER,
25 Container,
25 HIERARCHY,
26 Hierarchy,
26 CONTEXT,
27 Context,
27 CALL
28 Call
28 } No newline at end of file
29 }
30
31 export interface RegistrationWithServices {
32 services?: object;
33 }
34
35 export interface ServiceRegistration extends RegistrationWithServices {
36 $type?: string | Constructor;
37
38 $factory?: string | Factory;
39
40 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
41
42 params?;
43
44 inject?: object | object[];
45
46 cleanup: (instance) => void | string;
47 }
48
49 export interface ValueRegistration {
50 $value;
51 parse?: boolean;
52 }
53
54 export interface DependencyRegistration extends RegistrationWithServices {
55 $dependency: string;
56 lazy?: boolean;
57 optional?: boolean;
58 default?;
59 }
60
61 export function isServiceRegistration(x): x is ServiceRegistration {
62 return x && ("$type" in x || "$factory" in x);
63 }
64
65 export function isValueRegistration(x): x is ValueRegistration {
66 return x && "$value" in x;
67 }
68
69 export function isDependencyRegistration(x): x is DependencyRegistration {
70 return x && "$depdendency" in x;
71 }
General Comments 0
You need to be logged in to leave comments. Login now