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