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