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