##// END OF EJS Templates
sync
cin -
r123:264497557e79 ioc ts support
parent child
Show More
@@ -1,84 +1,84
1 import { primitive } from "../safe";
1 import { primitive } from "../safe";
2 import { TypeRegistration } from "./Configuration";
2 import { TypeRegistration } from "./Configuration";
3
3
4 export interface InjectOptions {
4 export interface InjectOptions {
5 lazy?: boolean;
5 lazy?: boolean;
6 }
6 }
7
7
8 export interface Dependency<K extends keyof any> {
8 export interface Dependency<K extends keyof any> {
9 $dependency: K;
9 $dependency: K;
10
10
11 lazy?: boolean;
11 lazy?: boolean;
12
12
13 }
13 }
14
14
15 export interface Lazy<K extends keyof any> extends Dependency<K> {
15 export interface Lazy<K extends keyof any> extends Dependency<K> {
16 lazy: true;
16 lazy: true;
17 }
17 }
18
18
19 type Compatible<T1, T2> = T2 extends T1 ? any : never;
19 type Compatible<T1, T2> = T2 extends T1 ? any : never;
20
20
21 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
21 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
22
22
23 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
23 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
24 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
24 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
25 WalkDependencies<D, S>;
25 WalkDependencies<D, S>;
26
26
27 type WalkDependencies<D, S> = D extends primitive ? D :
27 type WalkDependencies<D, S> = D extends primitive ? D :
28 { [K in keyof D]: ExtractDependency<D[K], S> };
28 { [K in keyof D]: ExtractDependency<D[K], S> };
29
29
30 export class Builder<T, S extends object> {
30 export class Builder<T, S extends object> {
31 declare<P extends any[]>(...args: P) {
31 declare<P extends any[]>(...args: P) {
32 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
32 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
33
33
34 };
34 };
35 }
35 }
36
36
37 inject<P extends any[]>(...args: P) {
37 inject<P extends any[]>(...args: P) {
38 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
38 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
39 target: X,
39 target: X,
40 memberName: M,
40 memberName: M,
41 descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>>
41 descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>>
42 ) => {
42 ) => {
43
43
44 };
44 };
45 }
45 }
46
46
47 getDescriptor(): TypeRegistration<new () => T, S> {
47 getDescriptor(): TypeRegistration<new () => T, S> {
48 throw new Error();
48 throw new Error();
49 }
49 }
50
50
51 }
51 }
52
52
53 export interface DependencyOptions<T> {
53 export interface DependencyOptions<T> {
54 optional?: boolean;
54 optional?: boolean;
55 default?: T;
55 default?: T;
56 }
56 }
57
57
58 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
58 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
59 lazy: true;
59 lazy: true;
60 }
60 }
61
61
62 interface Declaration<S extends object> {
62 interface Declaration<S extends object> {
63 define<T>(): Builder<T, S>;
63 define<T>(): Builder<T, S>;
64
64
65 dependency<K extends keyof S>(name: K, opts: LazyDependencyOptions<S[K]>): Lazy<K>;
65 dependency<K extends keyof S>(name: K, opts: LazyDependencyOptions<S[K]>): Lazy<K>;
66 dependency<K extends keyof S>(name: K, opts?: DependencyOptions<S[K]>): Dependency<K>;
66 dependency<K extends keyof S>(name: K, opts?: DependencyOptions<S[K]>): Dependency<K>;
67
67
68 config(): Config<S>;
68 configure(): Config<S>;
69 }
69 }
70
70
71 type ServiceModule<T, S extends object, M extends string = "service"> = {
71 type ServiceModule<T, S extends object, M extends string = "service"> = {
72 [m in M]: Builder<T, S>;
72 [m in M]: Builder<T, S>;
73 };
73 };
74
74
75 type PromiseOrValue<T> = PromiseLike<T> | T;
75 type PromiseOrValue<T> = PromiseLike<T> | T;
76
76
77
77 export interface Config<S extends object, Y extends keyof S = keyof S> {
78 export interface Config<S extends object, Y extends keyof S = keyof S> {
78 register<K extends Y>(name: K, builder: Builder<S[K], S>): Config<S, Exclude<Y, K>>;
79 register<K extends Y>(name: K, m: { $from: Promise<ServiceModule<S[K], S>> } | S[K]): Config<S, Exclude<Y, K>>;
79 register<K extends Y>(name: K, m: Promise<ServiceModule<S[K], S>>): Config<S, Exclude<Y, K>>;
80 register<K extends Y, M extends string>(name: K, m: { $from: Promise<ServiceModule<S[K], S, M>>, service: M }): Config<S, Exclude<Y, K>>;
80 register<K extends Y, M extends string>(name: K, m: Promise<ServiceModule<S[K], S, M>>, x: M): Config<S, Exclude<Y, K>>;
81 register<K extends Y, C extends new (...args: any) => S[K]>(name: K, d: TypeRegistration<C, S>): Config<S, Exclude<Y, K>>;
81
82 }
82 }
83
83
84 export declare function declare<S extends object>(): Declaration<S>;
84 export declare function declare<S extends object>(): Declaration<S>;
@@ -1,407 +1,408
1 import {
1 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 ContainerResolve
5 ContainerResolve
6 } from "./interfaces";
6 } from "./interfaces";
7
7
8 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
8 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { AggregateDescriptor } from "./AggregateDescriptor";
9 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { ValueDescriptor } from "./ValueDescriptor";
10 import { ValueDescriptor } from "./ValueDescriptor";
11 import { Container } from "./Container";
11 import { Container } from "./Container";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
15 import { TraceSource } from "../log/TraceSource";
15 import { TraceSource } from "../log/TraceSource";
16 import { ConfigError } from "./ConfigError";
16 import { ConfigError } from "./ConfigError";
17 import { Cancellation } from "../Cancellation";
17 import { Cancellation } from "../Cancellation";
18 import { makeResolver } from "./ResolverHelper";
18 import { makeResolver } from "./ResolverHelper";
19 import { ICancellation } from "../interfaces";
19 import { ICancellation } from "../interfaces";
20 import { isDescriptor } from "./traits";
20 import { isDescriptor } from "./traits";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
22
22
23 export interface RegistrationScope<S extends object> {
23 export interface RegistrationScope<S extends object> {
24
24
25 /** сервисы, которые регистрируются в контексте активации и таким образом
25 /** сервисы, которые регистрируются в контексте активации и таким образом
26 * могут переопределять ранее зарегистрированные сервисы. за это свойство
26 * могут переопределять ранее зарегистрированные сервисы. за это свойство
27 * нужно платить, кроме того порядок активации будет влиять на результат
27 * нужно платить, кроме того порядок активации будет влиять на результат
28 * разрешения зависимостей.
28 * разрешения зависимостей.
29 */
29 */
30 services?: RegistrationMap<S>;
30 services?: RegistrationMap<S>;
31 }
31 }
32
32
33 /**
33 /**
34 * Базовый интефейс конфигурации сервисов
34 * Базовый интефейс конфигурации сервисов
35 */
35 */
36 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
36 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
37
37
38 activation?: ActivationType;
38 activation?: ActivationType;
39
39
40 params?: any;
40 params?: any;
41
41
42 inject?: object | object[];
42 inject?: object | object[];
43
43
44 cleanup?: ((instance: T) => void) | string;
44 cleanup?: ((instance: T) => void) | string;
45 }
45 }
46
46
47 export interface TypeRegistration<C extends new () => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
47 export interface TypeRegistration<C extends new () => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
48 $type: string | C;
48 $type: string | C;
49 params?: ConstructorParameters<C>;
49 }
50 }
50
51
51 export interface FactoryRegistration<F extends () => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
52 export interface FactoryRegistration<F extends () => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
52 $factory: string | F;
53 $factory: string | F;
53 }
54 }
54
55
55 export interface ValueRegistration<T> {
56 export interface ValueRegistration<T> {
56 $value: T;
57 $value: T;
57 parse?: boolean;
58 parse?: boolean;
58 }
59 }
59
60
60 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
61 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
61 $dependency: K;
62 $dependency: K;
62 lazy?: boolean;
63 lazy?: boolean;
63 optional?: boolean;
64 optional?: boolean;
64 default?: ContainerResolve<S, K>;
65 default?: ContainerResolve<S, K>;
65 }
66 }
66
67
67 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
68 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
68 lazy: true;
69 lazy: true;
69 }
70 }
70
71
71 export type Registration<T, S extends object> = T extends primitive ? T :
72 export type Registration<T, S extends object> = T extends primitive ? T :
72 (
73 (
73 T |
74 T |
74 { [k in keyof T]: Registration<T[k], S> } |
75 { [k in keyof T]: Registration<T[k], S> } |
75 TypeRegistration<new () => T, S> |
76 TypeRegistration<new () => T, S> |
76 FactoryRegistration<() => T, S> |
77 FactoryRegistration<() => T, S> |
77 ValueRegistration<any> |
78 ValueRegistration<any> |
78 DependencyRegistration<S, keyof S>
79 DependencyRegistration<S, keyof S>
79 );
80 );
80
81
81 export type RegistrationMap<S extends object> = {
82 export type RegistrationMap<S extends object> = {
82 [k in keyof S]?: Registration<S[k], S>;
83 [k in keyof S]?: Registration<S[k], S>;
83 };
84 };
84
85
85 const _activationTypes: { [k in ActivationType]: number; } = {
86 const _activationTypes: { [k in ActivationType]: number; } = {
86 singleton: 1,
87 singleton: 1,
87 container: 2,
88 container: 2,
88 hierarchy: 3,
89 hierarchy: 3,
89 context: 4,
90 context: 4,
90 call: 5
91 call: 5
91 };
92 };
92
93
93 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
94 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
94 return (!isPrimitive(x)) && ("$type" in x);
95 return (!isPrimitive(x)) && ("$type" in x);
95 }
96 }
96
97
97 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
98 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
98 return (!isPrimitive(x)) && ("$factory" in x);
99 return (!isPrimitive(x)) && ("$factory" in x);
99 }
100 }
100
101
101 export function isValueRegistration(x: any): x is ValueRegistration<any> {
102 export function isValueRegistration(x: any): x is ValueRegistration<any> {
102 return (!isPrimitive(x)) && ("$value" in x);
103 return (!isPrimitive(x)) && ("$value" in x);
103 }
104 }
104
105
105 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
106 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
106 return (!isPrimitive(x)) && ("$dependency" in x);
107 return (!isPrimitive(x)) && ("$dependency" in x);
107 }
108 }
108
109
109 export function isActivationType(x: string): x is ActivationType {
110 export function isActivationType(x: string): x is ActivationType {
110 return typeof x === "string" && x in _activationTypes;
111 return typeof x === "string" && x in _activationTypes;
111 }
112 }
112
113
113 const trace = TraceSource.get("@implab/core/di/Configuration");
114 const trace = TraceSource.get("@implab/core/di/Configuration");
114 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
115 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
115 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
116 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
116 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
117 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
117 if (data instanceof Array) {
118 if (data instanceof Array) {
118 return Promise.all(map ? data.map(map) : data);
119 return Promise.all(map ? data.map(map) : data);
119 } else {
120 } else {
120 const keys = Object.keys(data);
121 const keys = Object.keys(data);
121
122
122 const o: any = {};
123 const o: any = {};
123
124
124 await Promise.all(keys.map(async k => {
125 await Promise.all(keys.map(async k => {
125 const v = map ? map(data[k], k) : data[k];
126 const v = map ? map(data[k], k) : data[k];
126 o[k] = isPromise(v) ? await v : v;
127 o[k] = isPromise(v) ? await v : v;
127 }));
128 }));
128
129
129 return o;
130 return o;
130 }
131 }
131 }
132 }
132
133
133 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
134 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
134
135
135 export class Configuration<S extends object> {
136 export class Configuration<S extends object> {
136
137
137 _hasInnerDescriptors = false;
138 _hasInnerDescriptors = false;
138
139
139 readonly _container: Container<S>;
140 readonly _container: Container<S>;
140
141
141 _path: Array<string>;
142 _path: Array<string>;
142
143
143 _configName: string | undefined;
144 _configName: string | undefined;
144
145
145 _require: ModuleResolver | undefined;
146 _require: ModuleResolver | undefined;
146
147
147 constructor(container: Container<S>) {
148 constructor(container: Container<S>) {
148 argumentNotNull(container, "container");
149 argumentNotNull(container, "container");
149 this._container = container;
150 this._container = container;
150 this._path = [];
151 this._path = [];
151 }
152 }
152
153
153 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
154 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
154 argumentNotEmptyString(moduleName, "moduleName");
155 argumentNotEmptyString(moduleName, "moduleName");
155
156
156 trace.log(
157 trace.log(
157 "loadConfiguration moduleName={0}, contextRequire={1}",
158 "loadConfiguration moduleName={0}, contextRequire={1}",
158 moduleName,
159 moduleName,
159 contextRequire ? typeof (contextRequire) : "<nil>"
160 contextRequire ? typeof (contextRequire) : "<nil>"
160 );
161 );
161
162
162 this._configName = moduleName;
163 this._configName = moduleName;
163
164
164 const r = await makeResolver(undefined, contextRequire);
165 const r = await makeResolver(undefined, contextRequire);
165
166
166 const config = await r(moduleName, ct);
167 const config = await r(moduleName, ct);
167
168
168 await this._applyConfiguration(
169 await this._applyConfiguration(
169 config,
170 config,
170 await makeResolver(moduleName, contextRequire),
171 await makeResolver(moduleName, contextRequire),
171 ct
172 ct
172 );
173 );
173 }
174 }
174
175
175 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
176 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
176 argumentNotNull(data, "data");
177 argumentNotNull(data, "data");
177
178
178 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
179 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
179 }
180 }
180
181
181 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
182 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
182 trace.log("applyConfiguration");
183 trace.log("applyConfiguration");
183
184
184 this._configName = "$";
185 this._configName = "$";
185
186
186 if (resolver)
187 if (resolver)
187 this._require = resolver;
188 this._require = resolver;
188
189
189 let services: PartialServiceMap<S>;
190 let services: PartialServiceMap<S>;
190
191
191 try {
192 try {
192 services = await this._visitRegistrations(data, "$");
193 services = await this._visitRegistrations(data, "$");
193 } catch (e) {
194 } catch (e) {
194 throw this._makeError(e);
195 throw this._makeError(e);
195 }
196 }
196
197
197 this._container.register(services);
198 this._container.register(services);
198 }
199 }
199
200
200 _makeError(inner: any) {
201 _makeError(inner: any) {
201 const e = new ConfigError("Failed to load configuration", inner);
202 const e = new ConfigError("Failed to load configuration", inner);
202 e.configName = this._configName || "<inline>";
203 e.configName = this._configName || "<inline>";
203 e.path = this._makePath();
204 e.path = this._makePath();
204 return e;
205 return e;
205 }
206 }
206
207
207 _makePath() {
208 _makePath() {
208 return this._path
209 return this._path
209 .reduce(
210 .reduce(
210 (prev, cur) => typeof cur === "number" ?
211 (prev, cur) => typeof cur === "number" ?
211 `${prev}[${cur}]` :
212 `${prev}[${cur}]` :
212 `${prev}.${cur}`
213 `${prev}.${cur}`
213 )
214 )
214 .toString();
215 .toString();
215 }
216 }
216
217
217 async _resolveType(moduleName: string, localName: string) {
218 async _resolveType(moduleName: string, localName: string) {
218 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
219 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
219 try {
220 try {
220 const m = await this._loadModule(moduleName);
221 const m = await this._loadModule(moduleName);
221 return localName ? get(localName, m) : m;
222 return localName ? get(localName, m) : m;
222 } catch (e) {
223 } catch (e) {
223 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
224 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
224 throw e;
225 throw e;
225 }
226 }
226 }
227 }
227
228
228 _loadModule(moduleName: string) {
229 _loadModule(moduleName: string) {
229 trace.debug("loadModule {0}", moduleName);
230 trace.debug("loadModule {0}", moduleName);
230 if (!this._require)
231 if (!this._require)
231 throw new Error("Module loader isn't specified");
232 throw new Error("Module loader isn't specified");
232
233
233 return this._require(moduleName);
234 return this._require(moduleName);
234 }
235 }
235
236
236 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
237 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
237 this._enter(name);
238 this._enter(name);
238
239
239 if (data.constructor &&
240 if (data.constructor &&
240 data.constructor.prototype !== Object.prototype)
241 data.constructor.prototype !== Object.prototype)
241 throw new Error("Configuration must be a simple object");
242 throw new Error("Configuration must be a simple object");
242
243
243 const services = await mapAll(data, async (v, k) => {
244 const services = await mapAll(data, async (v, k) => {
244 const d = await this._visit(v, k.toString());
245 const d = await this._visit(v, k.toString());
245 return isDescriptor(d) ? d : new AggregateDescriptor(d);
246 return isDescriptor(d) ? d : new AggregateDescriptor(d);
246 }) as PartialServiceMap<S>;
247 }) as PartialServiceMap<S>;
247
248
248 this._leave();
249 this._leave();
249
250
250 return services;
251 return services;
251 }
252 }
252
253
253 _enter(name: string) {
254 _enter(name: string) {
254 this._path.push(name.toString());
255 this._path.push(name.toString());
255 trace.debug(">{0}", name);
256 trace.debug(">{0}", name);
256 }
257 }
257
258
258 _leave() {
259 _leave() {
259 const name = this._path.pop();
260 const name = this._path.pop();
260 trace.debug("<{0}", name);
261 trace.debug("<{0}", name);
261 }
262 }
262
263
263 async _visit(data: any, name: string): Promise<any> {
264 async _visit(data: any, name: string): Promise<any> {
264 if (isPrimitive(data) || isDescriptor(data))
265 if (isPrimitive(data) || isDescriptor(data))
265 return data;
266 return data;
266
267
267 if (isDependencyRegistration<S>(data)) {
268 if (isDependencyRegistration<S>(data)) {
268 return this._visitDependencyRegistration(data, name);
269 return this._visitDependencyRegistration(data, name);
269 } else if (isValueRegistration(data)) {
270 } else if (isValueRegistration(data)) {
270 return this._visitValueRegistration(data, name);
271 return this._visitValueRegistration(data, name);
271 } else if (isTypeRegistration(data)) {
272 } else if (isTypeRegistration(data)) {
272 return this._visitTypeRegistration(data, name);
273 return this._visitTypeRegistration(data, name);
273 } else if (isFactoryRegistration(data)) {
274 } else if (isFactoryRegistration(data)) {
274 return this._visitFactoryRegistration(data, name);
275 return this._visitFactoryRegistration(data, name);
275 } else if (data instanceof Array) {
276 } else if (data instanceof Array) {
276 return this._visitArray(data, name);
277 return this._visitArray(data, name);
277 }
278 }
278
279
279 return this._visitObject(data, name);
280 return this._visitObject(data, name);
280 }
281 }
281
282
282 async _visitObject(data: any, name: string) {
283 async _visitObject(data: any, name: string) {
283 if (data.constructor &&
284 if (data.constructor &&
284 data.constructor.prototype !== Object.prototype)
285 data.constructor.prototype !== Object.prototype)
285 return new ValueDescriptor(data);
286 return new ValueDescriptor(data);
286
287
287 this._enter(name);
288 this._enter(name);
288
289
289 const v = await mapAll(data, delegate(this, "_visit"));
290 const v = await mapAll(data, delegate(this, "_visit"));
290
291
291 // TODO: handle inline descriptors properly
292 // TODO: handle inline descriptors properly
292 // const ex = {
293 // const ex = {
293 // activate(ctx) {
294 // activate(ctx) {
294 // const value = ctx.activate(this.prop, "prop");
295 // const value = ctx.activate(this.prop, "prop");
295 // // some code
296 // // some code
296 // },
297 // },
297 // // will be turned to ReferenceDescriptor
298 // // will be turned to ReferenceDescriptor
298 // prop: { $dependency: "depName" }
299 // prop: { $dependency: "depName" }
299 // };
300 // };
300
301
301 this._leave();
302 this._leave();
302 return v;
303 return v;
303 }
304 }
304
305
305 async _visitArray(data: any[], name: string) {
306 async _visitArray(data: any[], name: string) {
306 if (data.constructor &&
307 if (data.constructor &&
307 data.constructor.prototype !== Array.prototype)
308 data.constructor.prototype !== Array.prototype)
308 return new ValueDescriptor(data);
309 return new ValueDescriptor(data);
309
310
310 this._enter(name);
311 this._enter(name);
311
312
312 const v = await mapAll(data, delegate(this, "_visit"));
313 const v = await mapAll(data, delegate(this, "_visit"));
313 this._leave();
314 this._leave();
314
315
315 return v;
316 return v;
316 }
317 }
317
318
318 _makeServiceParams(data: ServiceRegistration<any, S>) {
319 _makeServiceParams(data: ServiceRegistration<any, S>) {
319 const opts: any = {
320 const opts: any = {
320 owner: this._container
321 owner: this._container
321 };
322 };
322 if (data.services)
323 if (data.services)
323 opts.services = this._visitRegistrations(data.services, "services");
324 opts.services = this._visitRegistrations(data.services, "services");
324
325
325 if (data.inject) {
326 if (data.inject) {
326 this._enter("inject");
327 this._enter("inject");
327 opts.inject = mapAll(
328 opts.inject = mapAll(
328 data.inject instanceof Array ?
329 data.inject instanceof Array ?
329 data.inject :
330 data.inject :
330 [data.inject],
331 [data.inject],
331 delegate(this, "_visitObject")
332 delegate(this, "_visitObject")
332 );
333 );
333 this._leave();
334 this._leave();
334 }
335 }
335
336
336 if ("params" in data)
337 if ("params" in data)
337 opts.params = data.params instanceof Array ?
338 opts.params = data.params instanceof Array ?
338 this._visitArray(data.params, "params") :
339 this._visitArray(data.params, "params") :
339 this._visit(data.params, "params");
340 this._visit(data.params, "params");
340
341
341 if (data.activation) {
342 if (data.activation) {
342 opts.activation = data.activation;
343 opts.activation = data.activation;
343 }
344 }
344
345
345 if (data.cleanup)
346 if (data.cleanup)
346 opts.cleanup = data.cleanup;
347 opts.cleanup = data.cleanup;
347
348
348 return opts;
349 return opts;
349 }
350 }
350
351
351 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
352 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
352 this._enter(name);
353 this._enter(name);
353 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
354 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
354 this._leave();
355 this._leave();
355 return d;
356 return d;
356 }
357 }
357
358
358 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
359 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
359 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
360 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
360 this._enter(name);
361 this._enter(name);
361 const options = {
362 const options = {
362 name: data.$dependency,
363 name: data.$dependency,
363 optional: data.optional,
364 optional: data.optional,
364 default: data.default,
365 default: data.default,
365 services: data.services && await this._visitRegistrations(data.services, "services")
366 services: data.services && await this._visitRegistrations(data.services, "services")
366 };
367 };
367 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
368 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
368 this._leave();
369 this._leave();
369 return d;
370 return d;
370 }
371 }
371
372
372 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
373 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
373 argumentNotNull(data.$type, "data.$type");
374 argumentNotNull(data.$type, "data.$type");
374 this._enter(name);
375 this._enter(name);
375
376
376 const opts = this._makeServiceParams(data);
377 const opts = this._makeServiceParams(data);
377 if (data.$type instanceof Function) {
378 if (data.$type instanceof Function) {
378 opts.type = data.$type;
379 opts.type = data.$type;
379 } else {
380 } else {
380 const [moduleName, typeName] = data.$type.split(":", 2);
381 const [moduleName, typeName] = data.$type.split(":", 2);
381 opts.type = this._resolveType(moduleName, typeName);
382 opts.type = this._resolveType(moduleName, typeName);
382 }
383 }
383
384
384 const d = new TypeServiceDescriptor<S, any, any[]>(
385 const d = new TypeServiceDescriptor<S, any, any[]>(
385 await mapAll(opts)
386 await mapAll(opts)
386 );
387 );
387
388
388 this._leave();
389 this._leave();
389
390
390 return d;
391 return d;
391 }
392 }
392
393
393 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
394 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
394 argumentOfType(data.$factory, Function, "data.$factory");
395 argumentOfType(data.$factory, Function, "data.$factory");
395 this._enter(name);
396 this._enter(name);
396
397
397 const opts = this._makeServiceParams(data);
398 const opts = this._makeServiceParams(data);
398 opts.factory = data.$factory;
399 opts.factory = data.$factory;
399
400
400 const d = new FactoryServiceDescriptor<S, any, any[]>(
401 const d = new FactoryServiceDescriptor<S, any, any[]>(
401 await mapAll(opts)
402 await mapAll(opts)
402 );
403 );
403
404
404 this._leave();
405 this._leave();
405 return d;
406 return d;
406 }
407 }
407 }
408 }
@@ -1,6 +1,15
1 import { config } from "./services";
1 import { configure } from "./services";
2 import { Foo } from "./Foo";
3 import { Bar } from "./Bar";
2
4
3 config()
5 export const config = configure()
4 .register("bar", import("./Bar"))
6 .register("bar", { $from: import("./Bar"), service: "service" })
5 .register("box", import("./Box"), "service");
7 .register("box", { $from: import("./Box") })
6 // .register("foo", import("./Foo"), "Foo");
8 .register("host", "example.com")
9 .register("foo", {
10 $type: Foo
11 })
12 .register("bar2", {
13 $type: Bar,
14 params: []
15 });
@@ -1,23 +1,25
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2 import { Bar } from "./Bar";
2 import { Bar } from "./Bar";
3 import { Box } from "./Box";
3 import { Box } from "./Box";
4 import { declare } from "../di/Annotations";
4 import { declare } from "../di/Annotations";
5
5
6 /**
6 /**
7 * Сервисы доступные внутри контейнера
7 * Сервисы доступные внутри контейнера
8 */
8 */
9 export interface Services {
9 export interface Services {
10 foo: Foo;
10 foo: Foo;
11
11
12 bar: Bar;
12 bar: Bar;
13
13
14 bar2: Bar;
15
14 box: Box<Bar>;
16 box: Box<Bar>;
15
17
16 host: string;
18 host: string;
17
19
18 }
20 }
19
21
20 /**
22 /**
21 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
23 * Экспортируем вспомогательные функции для описания сервисов и кинфогурации
22 */
24 */
23 export const { define, dependency, config } = declare<Services>();
25 export const { define, dependency, configure } = declare<Services>();
General Comments 0
You need to be logged in to leave comments. Login now