@adaas/a-concept 0.1.56 → 0.1.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/global/A-Dependency/A-Dependency-Flat.decorator.ts +68 -0
- package/src/global/A-Dependency/A-Dependency.class.ts +11 -0
- package/src/global/A-Dependency/A-Dependency.types.ts +6 -0
- package/src/global/A-Inject/A-Inject.types.ts +1 -0
- package/src/global/A-Scope/A-Scope.class.ts +79 -7
- package/src/global/A-Stage/A-Stage.class.ts +24 -15
- package/tests/A-Dependency.test.ts +198 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaas/a-concept",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.58",
|
|
4
4
|
"description": "A-Concept is a framework to build new Applications within or outside the ADAAS ecosystem. This framework is designed to be modular structure regardless environment and program goal.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
import { A_Context } from "@adaas/a-concept/global/A-Context/A-Context.class";
|
|
3
|
+
import { A_Meta } from "@adaas/a-concept/global/A-Meta/A-Meta.class";
|
|
4
|
+
import { A_TYPES__ComponentMetaKey } from "@adaas/a-concept/global/A-Component/A-Component.constants";
|
|
5
|
+
import { A_TYPES__ContainerMetaKey } from "@adaas/a-concept/global/A-Container/A-Container.constants";
|
|
6
|
+
import { A_TypeGuards } from "@adaas/a-concept/helpers/A_TypeGuards.helper";
|
|
7
|
+
import { A_TYPES__A_InjectDecorator_Meta, A_TYPES__InjectableTargets } from "../A-Inject/A-Inject.types";
|
|
8
|
+
import { A_TYPES__A_Dependency_FlatDecoratorReturn } from "./A-Dependency.types";
|
|
9
|
+
import { A_DependencyError } from "./A-Dependency.error";
|
|
10
|
+
import { A_CommonHelper } from "@adaas/a-concept/helpers/A_Common.helper";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Should indicate which dependency is required
|
|
15
|
+
*/
|
|
16
|
+
export function A_Dependency_Flat(): A_TYPES__A_Dependency_FlatDecoratorReturn {
|
|
17
|
+
|
|
18
|
+
return function (
|
|
19
|
+
target: A_TYPES__InjectableTargets,
|
|
20
|
+
methodName: string | symbol | undefined,
|
|
21
|
+
parameterIndex: number
|
|
22
|
+
) {
|
|
23
|
+
// for Error handling purposes
|
|
24
|
+
const componentName = A_CommonHelper.getComponentName(target)
|
|
25
|
+
|
|
26
|
+
if (!A_TypeGuards.isTargetAvailableForInjection(target)) {
|
|
27
|
+
throw new A_DependencyError(
|
|
28
|
+
A_DependencyError.InvalidDependencyTarget,
|
|
29
|
+
`A-Dependency cannot be used on the target of type ${typeof target} (${componentName})`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// determine the method name or 'constructor' for constructor injections
|
|
34
|
+
const method = methodName ? String(methodName) : 'constructor';
|
|
35
|
+
let metaKey;
|
|
36
|
+
|
|
37
|
+
switch (true) {
|
|
38
|
+
case A_TypeGuards.isComponentConstructor(target) || A_TypeGuards.isComponentInstance(target):
|
|
39
|
+
metaKey = A_TYPES__ComponentMetaKey.INJECTIONS;
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case A_TypeGuards.isContainerInstance(target):
|
|
43
|
+
metaKey = A_TYPES__ContainerMetaKey.INJECTIONS;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// get existing meta or create a new one
|
|
48
|
+
const existedMeta = A_Context.meta(target).get(metaKey) || new A_Meta();
|
|
49
|
+
// get existing injections for the method or create a new array
|
|
50
|
+
const paramsArray: A_TYPES__A_InjectDecorator_Meta = existedMeta.get(method) || [];
|
|
51
|
+
|
|
52
|
+
// set the parameter injection info
|
|
53
|
+
paramsArray[parameterIndex] = {
|
|
54
|
+
...(paramsArray[parameterIndex] || {}),
|
|
55
|
+
flat: true,
|
|
56
|
+
}
|
|
57
|
+
// save back the updated injections array
|
|
58
|
+
existedMeta.set(method, paramsArray);
|
|
59
|
+
|
|
60
|
+
// save back the updated meta info
|
|
61
|
+
A_Context
|
|
62
|
+
.meta(target)
|
|
63
|
+
.set(
|
|
64
|
+
metaKey,
|
|
65
|
+
existedMeta
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { A_Dependency_Default } from "./A-Dependency-Default.decorator";
|
|
2
|
+
import { A_Dependency_Flat } from "./A-Dependency-Flat.decorator";
|
|
2
3
|
import { A_Dependency_Load } from "./A-Dependency-Load.decorator";
|
|
3
4
|
import { A_Dependency_Parent } from "./A-Dependency-Parent.decorator";
|
|
4
5
|
import { A_Dependency_Require } from "./A-Dependency-Require.decorator";
|
|
@@ -41,6 +42,16 @@ export class A_Dependency {
|
|
|
41
42
|
return A_Dependency_Parent;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Allows to indicate that the dependency should be resolved in a flat manner
|
|
47
|
+
* Only in the same scope, without going up to parent scopes
|
|
48
|
+
*
|
|
49
|
+
* @returns
|
|
50
|
+
*/
|
|
51
|
+
static get Flat(): typeof A_Dependency_Flat {
|
|
52
|
+
return A_Dependency_Flat;
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
protected _name: string;
|
|
45
56
|
|
|
46
57
|
/**
|
|
@@ -32,4 +32,10 @@ export type A_TYPES__A_Dependency_ParentDecoratorReturn<T = any> = (
|
|
|
32
32
|
target: T,
|
|
33
33
|
propertyKey: string | symbol | undefined,
|
|
34
34
|
parameterIndex: number
|
|
35
|
+
) => void
|
|
36
|
+
|
|
37
|
+
export type A_TYPES__A_Dependency_FlatDecoratorReturn<T = any> = (
|
|
38
|
+
target: T,
|
|
39
|
+
propertyKey: string | symbol | undefined,
|
|
40
|
+
parameterIndex: number
|
|
35
41
|
) => void
|
|
@@ -200,6 +200,46 @@ export class A_Scope<
|
|
|
200
200
|
initializer.call(this, param1, param2);
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Generator to iterate through all parent scopes
|
|
205
|
+
*/
|
|
206
|
+
*parents(): Generator<A_Scope> {
|
|
207
|
+
let currentParent = this._parent;
|
|
208
|
+
while (currentParent) {
|
|
209
|
+
yield currentParent;
|
|
210
|
+
currentParent = currentParent._parent;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* This method is used to retrieve a parent scope at a specific level
|
|
216
|
+
*
|
|
217
|
+
* [!] Note that if the level is out of bounds, undefined is returned
|
|
218
|
+
* [!!] Uses negative values for levels (e.g. -1 for immediate parent, -2 for grandparent, etc.)
|
|
219
|
+
*
|
|
220
|
+
* @param level
|
|
221
|
+
* @returns
|
|
222
|
+
*/
|
|
223
|
+
parentOffset(
|
|
224
|
+
/**
|
|
225
|
+
* Level of the parent scope to retrieve
|
|
226
|
+
*
|
|
227
|
+
* Examples:
|
|
228
|
+
* - level 0 - immediate parent
|
|
229
|
+
* - level -1 - grandparent
|
|
230
|
+
* - level -2 - great-grandparent
|
|
231
|
+
*/
|
|
232
|
+
layerOffset: number
|
|
233
|
+
): A_Scope | undefined {
|
|
234
|
+
let parentScope = this.parent;
|
|
235
|
+
|
|
236
|
+
while (layerOffset < -1 && parentScope) {
|
|
237
|
+
parentScope = parentScope.parent;
|
|
238
|
+
layerOffset++;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return parentScope;
|
|
242
|
+
}
|
|
203
243
|
|
|
204
244
|
|
|
205
245
|
/**
|
|
@@ -461,7 +501,7 @@ export class A_Scope<
|
|
|
461
501
|
): boolean {
|
|
462
502
|
|
|
463
503
|
let found = this.hasFlat(ctor as any);
|
|
464
|
-
|
|
504
|
+
|
|
465
505
|
if (!found && !!this._parent)
|
|
466
506
|
try {
|
|
467
507
|
return this._parent.has(ctor as any);
|
|
@@ -1396,7 +1436,7 @@ export class A_Scope<
|
|
|
1396
1436
|
const componentName = A_CommonHelper.getComponentName(arg.target)
|
|
1397
1437
|
|
|
1398
1438
|
if ('instructions' in arg && !!arg.instructions) {
|
|
1399
|
-
const { target, instructions } = arg
|
|
1439
|
+
const { target, parent, flat, instructions } = arg
|
|
1400
1440
|
const dependency = this.resolve(target as any, instructions);
|
|
1401
1441
|
if (!dependency)
|
|
1402
1442
|
throw new A_ScopeError(
|
|
@@ -1406,17 +1446,47 @@ export class A_Scope<
|
|
|
1406
1446
|
|
|
1407
1447
|
return dependency;
|
|
1408
1448
|
} else {
|
|
1409
|
-
const { target, require, create, defaultArgs } = arg;
|
|
1449
|
+
const { target, require, create, defaultArgs, parent, flat, } = arg;
|
|
1410
1450
|
|
|
1411
|
-
let dependency
|
|
1451
|
+
let dependency;
|
|
1412
1452
|
|
|
1453
|
+
// ----------------- Resolution Strategies -----------------
|
|
1454
|
+
switch (true) {
|
|
1455
|
+
// 1) Flat resolution
|
|
1456
|
+
case flat: {
|
|
1457
|
+
dependency = this.resolveFlat(target);
|
|
1458
|
+
break;
|
|
1459
|
+
}
|
|
1460
|
+
// 2) Parent resolution
|
|
1461
|
+
case parent && typeof parent.layerOffset === 'number': {
|
|
1462
|
+
const targetParent = this.parentOffset(parent.layerOffset);
|
|
1463
|
+
if(!targetParent) {
|
|
1464
|
+
throw new A_ScopeError(
|
|
1465
|
+
A_ScopeError.ResolutionError,
|
|
1466
|
+
`Unable to resolve parent scope at offset ${parent.layerOffset} for dependency ${componentName} for component ${component.name} in scope ${this.name}`
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
dependency = targetParent.resolve(target);
|
|
1470
|
+
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
// 3) Normal resolution
|
|
1474
|
+
default: {
|
|
1475
|
+
dependency = this.resolve(target);
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// ----------------- Post-Resolution Actions -----------------
|
|
1481
|
+
|
|
1482
|
+
// 1) Create default instance in case when allowed
|
|
1413
1483
|
if (create && !dependency && A_TypeGuards.isAllowedForDependencyDefaultCreation(target)) {
|
|
1414
|
-
|
|
1484
|
+
dependency = new target(...defaultArgs);
|
|
1415
1485
|
|
|
1416
|
-
this.register(
|
|
1417
|
-
return newDependency;
|
|
1486
|
+
this.register(dependency);
|
|
1418
1487
|
}
|
|
1419
1488
|
|
|
1489
|
+
// 2) Throw error in case when required but not resolved
|
|
1420
1490
|
if (require && !dependency) {
|
|
1421
1491
|
throw new A_ScopeError(
|
|
1422
1492
|
A_ScopeError.ResolutionError,
|
|
@@ -1424,6 +1494,8 @@ export class A_Scope<
|
|
|
1424
1494
|
);
|
|
1425
1495
|
}
|
|
1426
1496
|
|
|
1497
|
+
|
|
1498
|
+
// Finally, return the dependency (either resolved or undefined)
|
|
1427
1499
|
return dependency;
|
|
1428
1500
|
}
|
|
1429
1501
|
});
|
|
@@ -140,29 +140,38 @@ export class A_Stage {
|
|
|
140
140
|
return this._feature;
|
|
141
141
|
|
|
142
142
|
default: {
|
|
143
|
-
const { target, require, create, defaultArgs, parent } = arg;
|
|
143
|
+
const { target, require, create, defaultArgs, parent, flat } = arg;
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
let dependency;
|
|
147
147
|
let targetScope = scope;
|
|
148
148
|
|
|
149
|
-
if (parent && typeof parent.layerOffset === 'number') {
|
|
150
|
-
let parentScope = scope.parent;
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
switch (true) {
|
|
151
|
+
// 1) Flat resolution
|
|
152
|
+
case flat: {
|
|
153
|
+
dependency = targetScope.resolveFlat(target);
|
|
154
|
+
break;
|
|
157
155
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
// 2) Parent resolution
|
|
157
|
+
case parent && typeof parent.layerOffset === 'number': {
|
|
158
|
+
const targetParent = targetScope.parentOffset(parent.layerOffset);
|
|
159
|
+
if (!targetParent) {
|
|
160
|
+
throw new A_StageError(
|
|
161
|
+
A_StageError.ArgumentsResolutionError,
|
|
162
|
+
`Unable to resolve parent scope at layer offset ${parent.layerOffset} for argument ${A_CommonHelper.getComponentName(arg.target)} for stage ${this.name} in scope ${scope.name}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
dependency = targetParent.resolve(target);
|
|
166
|
+
targetScope = targetParent;
|
|
167
|
+
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
// 3) Normal resolution
|
|
171
|
+
default: {
|
|
172
|
+
dependency = targetScope.resolve(target);
|
|
173
|
+
break;
|
|
162
174
|
}
|
|
163
|
-
|
|
164
|
-
} else {
|
|
165
|
-
dependency = targetScope.resolve(target);
|
|
166
175
|
}
|
|
167
176
|
|
|
168
177
|
if (create && !dependency && A_TypeGuards.isAllowedForDependencyDefaultCreation(target)) {
|
|
@@ -89,5 +89,203 @@ describe('A-Dependency tests', () => {
|
|
|
89
89
|
expect(instance!.component).toBeInstanceOf(MyCustomEntity);
|
|
90
90
|
expect(instance!.component.foo).toBe('bar');
|
|
91
91
|
});
|
|
92
|
+
it('Should resolve only dependency on the same level with Flat directive', async () => {
|
|
93
|
+
class ParentComponent extends A_Component { }
|
|
94
|
+
|
|
95
|
+
class ChildComponent extends A_Component {
|
|
96
|
+
constructor(
|
|
97
|
+
@A_Dependency.Flat()
|
|
98
|
+
@A_Inject(ParentComponent) public parentComponent?: ParentComponent,
|
|
99
|
+
) {
|
|
100
|
+
super();
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const parentScope = new A_Scope({ components: [ParentComponent] });
|
|
106
|
+
const childScope = new A_Scope({ components: [ChildComponent] }, { parent: parentScope });
|
|
107
|
+
|
|
108
|
+
const instance = childScope.resolve(ChildComponent);
|
|
109
|
+
|
|
110
|
+
expect(instance).toBeDefined();
|
|
111
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
112
|
+
expect(instance!.parentComponent).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('Should resolve create dependency with Create directive', async () => {
|
|
116
|
+
|
|
117
|
+
class MyEntity_A extends A_Entity<{ name: string }> {
|
|
118
|
+
name!: string;
|
|
119
|
+
|
|
120
|
+
fromNew(newEntity: { name: string; }): void {
|
|
121
|
+
super.fromNew(newEntity);
|
|
122
|
+
this.name = newEntity.name;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
class ChildComponent extends A_Component {
|
|
127
|
+
constructor(
|
|
128
|
+
@A_Dependency.Default({ name: 'Entity A' })
|
|
129
|
+
@A_Inject(MyEntity_A) public myEntityA: MyEntity_A,
|
|
130
|
+
) {
|
|
131
|
+
super();
|
|
132
|
+
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const childScope = new A_Scope({ components: [ChildComponent], entities: [MyEntity_A] });
|
|
137
|
+
|
|
138
|
+
const instance = childScope.resolve(ChildComponent);
|
|
139
|
+
|
|
140
|
+
expect(instance).toBeDefined();
|
|
141
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
142
|
+
expect(instance!.myEntityA).toBeInstanceOf(MyEntity_A);
|
|
143
|
+
expect(instance!.myEntityA.name).toBe('Entity A');
|
|
144
|
+
});
|
|
145
|
+
it('Should resolve Parent entity if it exists, even if Default provided', async () => {
|
|
146
|
+
|
|
147
|
+
class MyEntity_A extends A_Entity<{ name: string }> {
|
|
148
|
+
name!: string;
|
|
149
|
+
|
|
150
|
+
fromNew(newEntity: { name: string; }): void {
|
|
151
|
+
super.fromNew(newEntity);
|
|
152
|
+
this.name = newEntity.name;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
class ChildComponent extends A_Component {
|
|
157
|
+
constructor(
|
|
158
|
+
@A_Dependency.Default({ name: 'Child Entity' })
|
|
159
|
+
@A_Inject(MyEntity_A) public myEntityA: MyEntity_A,
|
|
160
|
+
) {
|
|
161
|
+
super();
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const parentScope = new A_Scope({ entities: [new MyEntity_A({ name: 'Parent Entity' })] });
|
|
167
|
+
const childScope = new A_Scope({ components: [ChildComponent] }, { parent: parentScope });
|
|
168
|
+
|
|
169
|
+
const instance = childScope.resolve(ChildComponent);
|
|
170
|
+
|
|
171
|
+
expect(instance).toBeDefined();
|
|
172
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
173
|
+
expect(instance!.myEntityA).toBeInstanceOf(MyEntity_A);
|
|
174
|
+
expect(instance!.myEntityA.name).toBe('Parent Entity');
|
|
175
|
+
});
|
|
176
|
+
it('Should resolve dependencies properly with combination of Directives', async () => {
|
|
177
|
+
|
|
178
|
+
class MyEntity_A extends A_Entity<{ name: string }> {
|
|
179
|
+
name!: string;
|
|
180
|
+
|
|
181
|
+
fromNew(newEntity: { name: string; }): void {
|
|
182
|
+
super.fromNew(newEntity);
|
|
183
|
+
this.name = newEntity.name;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
class ChildComponent extends A_Component {
|
|
188
|
+
constructor(
|
|
189
|
+
@A_Dependency.Flat()
|
|
190
|
+
@A_Dependency.Default({ name: 'Child Entity' })
|
|
191
|
+
@A_Inject(MyEntity_A) public myEntityA: MyEntity_A,
|
|
192
|
+
) {
|
|
193
|
+
super();
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const parentScope = new A_Scope({ entities: [new MyEntity_A({ name: 'Parent Entity' })] });
|
|
199
|
+
const childScope = new A_Scope({ components: [ChildComponent] }, { parent: parentScope });
|
|
200
|
+
|
|
201
|
+
const instance = childScope.resolve(ChildComponent);
|
|
202
|
+
|
|
203
|
+
expect(instance).toBeDefined();
|
|
204
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
205
|
+
expect(instance!.myEntityA).toBeInstanceOf(MyEntity_A);
|
|
206
|
+
expect(instance!.myEntityA.name).toBe('Child Entity');
|
|
207
|
+
})
|
|
208
|
+
it('Should resolve dependency from parent scope with Parent Directive', async () => {
|
|
209
|
+
|
|
210
|
+
class MyEntity_A extends A_Entity<{ name: string }> {
|
|
211
|
+
name!: string;
|
|
212
|
+
|
|
213
|
+
fromNew(newEntity: { name: string; }): void {
|
|
214
|
+
super.fromNew(newEntity);
|
|
215
|
+
this.name = newEntity.name;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
class ChildComponent extends A_Component {
|
|
220
|
+
constructor(
|
|
221
|
+
@A_Dependency.Parent()
|
|
222
|
+
@A_Inject(MyEntity_A) public myEntityA: MyEntity_A,
|
|
223
|
+
) {
|
|
224
|
+
super();
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const parentScope = new A_Scope({
|
|
230
|
+
name: 'Parent Scope',
|
|
231
|
+
entities: [new MyEntity_A({ name: 'Parent Entity' })]
|
|
232
|
+
});
|
|
233
|
+
const childScope = new A_Scope({
|
|
234
|
+
name: 'Child Scope',
|
|
235
|
+
components: [ChildComponent],
|
|
236
|
+
entities: [new MyEntity_A({ name: 'Child Entity' })]
|
|
237
|
+
}, { parent: parentScope });
|
|
238
|
+
|
|
239
|
+
const instance = childScope.resolve(ChildComponent);
|
|
240
|
+
|
|
241
|
+
expect(instance).toBeDefined();
|
|
242
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
243
|
+
expect(instance!.myEntityA).toBeInstanceOf(MyEntity_A);
|
|
244
|
+
expect(instance!.myEntityA.name).toBe('Parent Entity');
|
|
245
|
+
});
|
|
246
|
+
it('Should resolve dependency from parent scope with Parent Directive and offset', async () => {
|
|
247
|
+
|
|
248
|
+
class MyEntity_A extends A_Entity<{ name: string }> {
|
|
249
|
+
name!: string;
|
|
250
|
+
|
|
251
|
+
fromNew(newEntity: { name: string; }): void {
|
|
252
|
+
super.fromNew(newEntity);
|
|
253
|
+
this.name = newEntity.name;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
class ChildComponent extends A_Component {
|
|
258
|
+
constructor(
|
|
259
|
+
@A_Dependency.Parent(-2)
|
|
260
|
+
@A_Inject(MyEntity_A) public myEntityA: MyEntity_A,
|
|
261
|
+
) {
|
|
262
|
+
super();
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const grandparentScope = new A_Scope({
|
|
268
|
+
name: 'Grandparent Scope',
|
|
269
|
+
entities: [new MyEntity_A({ name: 'Grandparent Entity' })]
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const parentScope = new A_Scope({
|
|
273
|
+
name: 'Parent Scope',
|
|
274
|
+
entities: [new MyEntity_A({ name: 'Parent Entity' })]
|
|
275
|
+
}, { parent: grandparentScope });
|
|
276
|
+
|
|
277
|
+
const childScope = new A_Scope({
|
|
278
|
+
name: 'Child Scope',
|
|
279
|
+
components: [ChildComponent],
|
|
280
|
+
entities: [new MyEntity_A({ name: 'Child Entity' })]
|
|
281
|
+
}, { parent: parentScope });
|
|
282
|
+
|
|
283
|
+
const instance = childScope.resolve(ChildComponent);
|
|
284
|
+
|
|
285
|
+
expect(instance).toBeDefined();
|
|
286
|
+
expect(instance).toBeInstanceOf(ChildComponent);
|
|
287
|
+
expect(instance!.myEntityA).toBeInstanceOf(MyEntity_A);
|
|
288
|
+
expect(instance!.myEntityA.name).toBe('Grandparent Entity');
|
|
289
|
+
});
|
|
92
290
|
|
|
93
291
|
});
|