@joist/di 4.0.0-next.1 → 4.0.0-next.11
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/README.md +52 -34
- package/package.json +4 -14
- package/src/lib/dom-injector.test.ts +16 -18
- package/src/lib/dom-injector.ts +3 -4
- package/src/lib/inject.test.ts +33 -56
- package/src/lib/inject.ts +4 -6
- package/src/lib/injectable-el.test.ts +130 -0
- package/src/lib/injectable-el.ts +63 -0
- package/src/lib/injectable.test.ts +19 -116
- package/src/lib/injectable.ts +21 -57
- package/src/lib/injector.test.ts +132 -130
- package/src/lib/injector.ts +33 -19
- package/src/lib/lifecycle.test.ts +68 -64
- package/src/lib/lifecycle.ts +19 -4
- package/src/lib/metadata.ts +12 -0
- package/src/lib/provider.ts +16 -8
- package/src/lib.ts +1 -1
- package/target/lib/dom-injector.js +3 -4
- package/target/lib/dom-injector.js.map +1 -1
- package/target/lib/dom-injector.test.js +16 -18
- package/target/lib/dom-injector.test.js.map +1 -1
- package/target/lib/inject.js +3 -3
- package/target/lib/inject.js.map +1 -1
- package/target/lib/inject.test.js +58 -90
- package/target/lib/inject.test.js.map +1 -1
- package/target/lib/injectable-el.d.ts +334 -0
- package/target/lib/injectable-el.js +40 -0
- package/target/lib/injectable-el.js.map +1 -0
- package/target/lib/injectable-el.test.js +238 -0
- package/target/lib/injectable-el.test.js.map +1 -0
- package/target/lib/injectable.d.ts +5 -7
- package/target/lib/injectable.js +13 -42
- package/target/lib/injectable.js.map +1 -1
- package/target/lib/injectable.test.js +49 -204
- package/target/lib/injectable.test.js.map +1 -1
- package/target/lib/injector.d.ts +1 -1
- package/target/lib/injector.js +18 -14
- package/target/lib/injector.js.map +1 -1
- package/target/lib/injector.test.js +215 -216
- package/target/lib/injector.test.js.map +1 -1
- package/target/lib/lifecycle.d.ts +2 -4
- package/target/lib/lifecycle.js +15 -4
- package/target/lib/lifecycle.js.map +1 -1
- package/target/lib/lifecycle.test.js +142 -123
- package/target/lib/lifecycle.test.js.map +1 -1
- package/target/lib/metadata.d.ts +6 -0
- package/target/lib/metadata.js +5 -0
- package/target/lib/metadata.js.map +1 -0
- package/target/lib/provider.d.ts +6 -5
- package/target/lib/provider.js +10 -4
- package/target/lib/provider.js.map +1 -1
- package/target/lib.d.ts +1 -1
- package/target/lib.js +1 -1
- package/target/lib.js.map +1 -1
- package/src/lib/injectable-map.test.ts +0 -18
- package/src/lib/injectable-map.ts +0 -3
- package/src/lib/injector.test-node.ts +0 -187
- package/target/lib/injectable-map.d.ts +0 -3
- package/target/lib/injectable-map.js +0 -3
- package/target/lib/injectable-map.js.map +0 -1
- package/target/lib/injectable-map.test.js +0 -15
- package/target/lib/injectable-map.test.js.map +0 -1
- package/target/lib/injector.test-node.d.ts +0 -1
- package/target/lib/injector.test-node.js +0 -231
- package/target/lib/injector.test-node.js.map +0 -1
- /package/target/lib/{injectable-map.test.d.ts → injectable-el.test.d.ts} +0 -0
|
@@ -1,128 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assert } from 'chai';
|
|
2
2
|
|
|
3
3
|
import { injectable } from './injectable.js';
|
|
4
4
|
import { inject } from './inject.js';
|
|
5
|
+
import { injectables } from './injector.js';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class Foo {}
|
|
9
|
-
class Bar {}
|
|
7
|
+
it('should locally override a provider', () => {
|
|
8
|
+
class Foo {}
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
class MyElement extends HTMLElement {
|
|
13
|
-
foo = inject(Foo);
|
|
14
|
-
bar = inject(Bar);
|
|
15
|
-
}
|
|
10
|
+
class Bar extends Foo {}
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
@injectable({
|
|
13
|
+
providers: [{ provide: Foo, use: Bar }]
|
|
14
|
+
})
|
|
15
|
+
class MyService {
|
|
16
|
+
foo = inject(Foo);
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const el = new MyService();
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
it('should locally override a provider', () => {
|
|
25
|
-
class Foo {}
|
|
26
|
-
|
|
27
|
-
class Bar extends Foo {}
|
|
28
|
-
|
|
29
|
-
const MyElement = injectable(
|
|
30
|
-
class {
|
|
31
|
-
static providers = [{ provide: Foo, use: Bar }];
|
|
32
|
-
|
|
33
|
-
foo = inject(Foo);
|
|
34
|
-
}
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const el = new MyElement();
|
|
38
|
-
|
|
39
|
-
expect(el.foo()).to.be.instanceOf(Bar);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should handle parent HTML Injectors', async () => {
|
|
43
|
-
@injectable
|
|
44
|
-
class A {}
|
|
45
|
-
|
|
46
|
-
const B = injectable(
|
|
47
|
-
class {
|
|
48
|
-
a = inject(A);
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
class AltA implements A {}
|
|
53
|
-
|
|
54
|
-
@injectable
|
|
55
|
-
class Parent extends HTMLElement {
|
|
56
|
-
static providers = [
|
|
57
|
-
{ provide: B, use: B },
|
|
58
|
-
{ provide: A, use: AltA }
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@injectable
|
|
63
|
-
class Child extends HTMLElement {
|
|
64
|
-
b = inject(B);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
customElements.define('injectable-parent-1', Parent);
|
|
68
|
-
customElements.define('injectable-child-1', Child);
|
|
69
|
-
|
|
70
|
-
const el = await fixture(html`
|
|
71
|
-
<injectable-parent-1>
|
|
72
|
-
<injectable-child-1></injectable-child-1>
|
|
73
|
-
</injectable-parent-1>
|
|
74
|
-
`);
|
|
75
|
-
|
|
76
|
-
const child = el.querySelector<Child>('injectable-child-1')!;
|
|
77
|
-
|
|
78
|
-
expect(child.b().a()).to.be.instanceOf(AltA);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should handle changing contexts', async () => {
|
|
82
|
-
class A {}
|
|
83
|
-
class AltA implements A {}
|
|
84
|
-
|
|
85
|
-
@injectable
|
|
86
|
-
class Ctx1 extends HTMLElement {
|
|
87
|
-
static providers = [{ provide: A, use: A }];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@injectable
|
|
91
|
-
class Ctx2 extends HTMLElement {
|
|
92
|
-
static providers = [{ provide: A, use: AltA }];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
@injectable
|
|
96
|
-
class Child extends HTMLElement {
|
|
97
|
-
a = inject(A);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
customElements.define('ctx-1', Ctx1);
|
|
101
|
-
customElements.define('ctx-2', Ctx2);
|
|
102
|
-
customElements.define('ctx-child', Child);
|
|
103
|
-
|
|
104
|
-
const el = await fixture(html`
|
|
105
|
-
<div>
|
|
106
|
-
<ctx-1>
|
|
107
|
-
<ctx-child></ctx-child>
|
|
108
|
-
</ctx-1>
|
|
109
|
-
|
|
110
|
-
<ctx-2></ctx-2>
|
|
111
|
-
</div>
|
|
112
|
-
`);
|
|
113
|
-
|
|
114
|
-
const ctx2 = el.querySelector('ctx-2')!;
|
|
115
|
-
|
|
116
|
-
let child = el.querySelector<Child>('ctx-child')!;
|
|
117
|
-
|
|
118
|
-
expect(child.a()).to.be.instanceOf(A);
|
|
119
|
-
|
|
120
|
-
child.remove();
|
|
21
|
+
assert.instanceOf(el.foo(), Bar);
|
|
22
|
+
});
|
|
121
23
|
|
|
122
|
-
|
|
24
|
+
it('should define an injector for a service instance', () => {
|
|
25
|
+
@injectable()
|
|
26
|
+
class MyService {}
|
|
123
27
|
|
|
124
|
-
|
|
28
|
+
const instance = new MyService();
|
|
125
29
|
|
|
126
|
-
|
|
127
|
-
});
|
|
30
|
+
assert.ok(injectables.has(instance));
|
|
128
31
|
});
|
package/src/lib/injectable.ts
CHANGED
|
@@ -1,69 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
import { Injector } from './injector.js';
|
|
3
|
-
import { InjectableMap } from './injectable-map.js';
|
|
1
|
+
(Symbol as any).metadata ??= Symbol('Symbol.metadata');
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
import { ConstructableToken, Provider } from './provider.js';
|
|
4
|
+
import { injectables, Injector } from './injector.js';
|
|
5
|
+
import { injectableEl } from './injectable-el.js';
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
super();
|
|
11
|
-
|
|
12
|
-
// Define a new Injector and assiciate it with this instance of the service
|
|
13
|
-
const injector = new Injector(Base.providers);
|
|
14
|
-
INJECTABLE_MAP.set(this, injector);
|
|
7
|
+
export interface InjectableOpts {
|
|
8
|
+
providers?: Provider<unknown>[];
|
|
9
|
+
}
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
export function injectable(opts?: InjectableOpts) {
|
|
12
|
+
return function injectableDecorator<T extends ConstructableToken<any>>(
|
|
13
|
+
Base: T,
|
|
14
|
+
ctx: ClassDecoratorContext
|
|
15
|
+
) {
|
|
16
|
+
class Injectable extends Base {
|
|
17
|
+
constructor(..._: any[]) {
|
|
18
|
+
super();
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
injector.setParent(parentInjector);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
20
|
+
injectables.set(this, new Injector(opts?.providers));
|
|
26
21
|
}
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (super.connectedCallback) {
|
|
34
|
-
super.connectedCallback();
|
|
35
|
-
}
|
|
24
|
+
// Only apply custom element bootstrap logic if the decorated class is an HTMLElement
|
|
25
|
+
if ('HTMLElement' in globalThis) {
|
|
26
|
+
if (HTMLElement.prototype.isPrototypeOf(Base.prototype)) {
|
|
27
|
+
return injectableEl(Injectable, ctx);
|
|
36
28
|
}
|
|
37
29
|
}
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
const injector = INJECTABLE_MAP.get(this);
|
|
41
|
-
|
|
42
|
-
if (injector) {
|
|
43
|
-
injector.setParent(undefined);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (super.disconnectedCallback) {
|
|
47
|
-
super.disconnectedCallback();
|
|
48
|
-
}
|
|
49
|
-
}
|
|
31
|
+
return Injectable;
|
|
50
32
|
};
|
|
51
33
|
}
|
|
52
|
-
|
|
53
|
-
function findInjectorRoot(e: Event): Injector | null {
|
|
54
|
-
const path = e.composedPath();
|
|
55
|
-
|
|
56
|
-
// find firt parent
|
|
57
|
-
// skips the first item which is the target
|
|
58
|
-
for (let i = 1; i < path.length; i++) {
|
|
59
|
-
const part = path[i];
|
|
60
|
-
|
|
61
|
-
const injector = INJECTABLE_MAP.get(part);
|
|
62
|
-
|
|
63
|
-
if (injector) {
|
|
64
|
-
return injector;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return null;
|
|
69
|
-
}
|
package/src/lib/injector.test.ts
CHANGED
|
@@ -1,184 +1,186 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assert } from 'chai';
|
|
2
2
|
|
|
3
3
|
import { Injector } from './injector.js';
|
|
4
4
|
import { inject } from './inject.js';
|
|
5
5
|
import { injectable } from './injectable.js';
|
|
6
6
|
import { Provider, StaticToken } from './provider.js';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class A {}
|
|
8
|
+
it('should create a new instance of a single provider', () => {
|
|
9
|
+
class A {}
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
const app = new Injector();
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
expect(app.inject(A)).to.equal(app.inject(A));
|
|
16
|
-
});
|
|
13
|
+
assert(app.inject(A) instanceof A);
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class B {}
|
|
15
|
+
assert.equal(app.inject(A), app.inject(A));
|
|
16
|
+
});
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
b = inject(B);
|
|
26
|
-
}
|
|
18
|
+
it('should inject providers in the correct order', () => {
|
|
19
|
+
class A {}
|
|
20
|
+
class B {}
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
@injectable()
|
|
23
|
+
class MyService {
|
|
24
|
+
a = inject(A);
|
|
25
|
+
b = inject(B);
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
28
|
+
const app = new Injector();
|
|
29
|
+
const instance = app.inject(MyService);
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
assert(instance.a() instanceof A);
|
|
32
|
+
assert(instance.b() instanceof B);
|
|
33
|
+
});
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
a = inject(A);
|
|
41
|
-
}
|
|
35
|
+
it('should create a new instance of a provider that has a full dep tree', () => {
|
|
36
|
+
class A {}
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
@injectable()
|
|
39
|
+
class B {
|
|
40
|
+
a = inject(A);
|
|
41
|
+
}
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
@injectable()
|
|
44
|
+
class C {
|
|
45
|
+
b = inject(B);
|
|
46
|
+
}
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
@injectable()
|
|
49
|
+
class D {
|
|
50
|
+
c = inject(C);
|
|
51
|
+
}
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
@injectable()
|
|
54
|
+
class E {
|
|
55
|
+
d = inject(D);
|
|
56
|
+
}
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
const app = new Injector();
|
|
59
|
+
const instance = app.inject(E);
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
assert(instance.d().c().b().a() instanceof A);
|
|
62
|
+
});
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
a = inject(A);
|
|
70
|
-
}
|
|
64
|
+
it('should override a provider if explicitly instructed', () => {
|
|
65
|
+
class A {}
|
|
71
66
|
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
@injectable()
|
|
68
|
+
class B {
|
|
69
|
+
a = inject(A);
|
|
70
|
+
}
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
});
|
|
72
|
+
class AltA extends A {}
|
|
73
|
+
const app = new Injector([{ provide: A, use: AltA }]);
|
|
77
74
|
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
assert(app.inject(B).a() instanceof AltA);
|
|
76
|
+
});
|
|
80
77
|
|
|
81
|
-
|
|
78
|
+
it('should return an existing instance from a parent injector', () => {
|
|
79
|
+
class A {}
|
|
82
80
|
|
|
83
|
-
|
|
81
|
+
const parent = new Injector();
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
});
|
|
83
|
+
const app = new Injector([], parent);
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
assert.equal(parent.inject(A), app.inject(A));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should use a factory if provided', () => {
|
|
89
|
+
class Service {
|
|
90
|
+
hello() {
|
|
91
|
+
return 'world';
|
|
93
92
|
}
|
|
93
|
+
}
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
95
|
+
const injector = new Injector([
|
|
96
|
+
{
|
|
97
|
+
provide: Service,
|
|
98
|
+
factory() {
|
|
99
|
+
return {
|
|
100
|
+
hello() {
|
|
101
|
+
return 'world';
|
|
102
|
+
}
|
|
103
|
+
};
|
|
105
104
|
}
|
|
106
|
-
|
|
105
|
+
}
|
|
106
|
+
]);
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
assert.equal(injector.inject(Service).hello(), 'world');
|
|
109
|
+
});
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
111
|
+
it('should throw an error if provider is missing both factory and use', () => {
|
|
112
|
+
class Service {
|
|
113
|
+
hello() {
|
|
114
|
+
return 'world';
|
|
116
115
|
}
|
|
116
|
+
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
const injector = new Injector([
|
|
119
|
+
{
|
|
120
|
+
provide: Service
|
|
121
|
+
}
|
|
122
|
+
]);
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
assert.throws(
|
|
125
|
+
() => injector.inject(Service),
|
|
126
|
+
"Provider for Service found but is missing either 'use' or 'factory'"
|
|
127
|
+
);
|
|
128
|
+
});
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
130
|
+
it('should pass factories and instance of the injector', async () => {
|
|
131
|
+
class Service {
|
|
132
|
+
hello() {
|
|
133
|
+
return 'world';
|
|
134
134
|
}
|
|
135
|
+
}
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
{
|
|
138
|
-
provide: Service,
|
|
139
|
-
factory(i) {
|
|
140
|
-
expect(i).to.equal(injector);
|
|
137
|
+
let factoryInjector: Injector | null = null;
|
|
141
138
|
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
const injector = new Injector([
|
|
140
|
+
{
|
|
141
|
+
provide: Service,
|
|
142
|
+
factory(i) {
|
|
143
|
+
factoryInjector = i;
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
}
|
|
146
|
+
]);
|
|
146
147
|
|
|
147
|
-
|
|
148
|
-
});
|
|
148
|
+
injector.inject(Service);
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const injector = new Injector();
|
|
150
|
+
assert.equal(factoryInjector, injector);
|
|
151
|
+
});
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
it('should create an instance from a StaticToken factory', () => {
|
|
154
|
+
const TOKEN = new StaticToken('test', () => 'Hello World');
|
|
155
|
+
const injector = new Injector();
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
});
|
|
157
|
+
const res = injector.inject(TOKEN);
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const injector = new Injector();
|
|
159
|
+
assert.equal(res, 'Hello World');
|
|
160
|
+
});
|
|
162
161
|
|
|
163
|
-
|
|
162
|
+
it('should create an instance from an async StaticToken factory', async () => {
|
|
163
|
+
const TOKEN = new StaticToken('test', () => Promise.resolve('Hello World'));
|
|
164
|
+
const injector = new Injector();
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
});
|
|
166
|
+
const res = await injector.inject(TOKEN);
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
assert.equal(res, 'Hello World');
|
|
169
|
+
});
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
it('should allow static token to be overridden', () => {
|
|
172
|
+
const TOKEN = new StaticToken<string>('test');
|
|
173
|
+
|
|
174
|
+
const provider: Provider<string> = {
|
|
175
|
+
provide: TOKEN,
|
|
176
|
+
factory() {
|
|
177
|
+
return 'Hello World';
|
|
178
|
+
}
|
|
179
|
+
};
|
|
177
180
|
|
|
178
|
-
|
|
181
|
+
const injector = new Injector([provider]);
|
|
179
182
|
|
|
180
|
-
|
|
183
|
+
const res = injector.inject(TOKEN);
|
|
181
184
|
|
|
182
|
-
|
|
183
|
-
});
|
|
185
|
+
assert.equal(res, 'Hello World');
|
|
184
186
|
});
|
package/src/lib/injector.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { readMetadata } from './metadata.js';
|
|
2
|
+
import {
|
|
3
|
+
ConstructableToken,
|
|
4
|
+
InjectionToken,
|
|
5
|
+
Provider,
|
|
6
|
+
ProviderFactory,
|
|
7
|
+
StaticToken
|
|
8
|
+
} from './provider.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Keeps track of all Injectable services and their Injector
|
|
12
|
+
*/
|
|
13
|
+
export const injectables = new WeakMap<object, Injector>();
|
|
4
14
|
|
|
5
15
|
/**
|
|
6
16
|
* Injectors create and store instances of services.
|
|
@@ -31,17 +41,17 @@ export class Injector {
|
|
|
31
41
|
this.providers = providers;
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
inject<T>(token: InjectionToken<T>): T {
|
|
35
|
-
return this.get(token);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
44
|
// resolves and retuns and instance of the requested service
|
|
39
|
-
|
|
45
|
+
inject<T>(token: InjectionToken<T>): T {
|
|
40
46
|
// check for a local instance
|
|
41
47
|
if (this.#instances.has(token)) {
|
|
42
48
|
const instance = this.#instances.get(token)!;
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
const metadata = readMetadata(token as ConstructableToken<T>);
|
|
51
|
+
|
|
52
|
+
if (metadata) {
|
|
53
|
+
callLifecycle(instance, metadata.onInjected);
|
|
54
|
+
}
|
|
45
55
|
|
|
46
56
|
return instance;
|
|
47
57
|
}
|
|
@@ -89,7 +99,7 @@ export class Injector {
|
|
|
89
99
|
this.#instances = new WeakMap();
|
|
90
100
|
}
|
|
91
101
|
|
|
92
|
-
#createAndCache<T>(token: InjectionToken<T>, factory:
|
|
102
|
+
#createAndCache<T>(token: InjectionToken<T>, factory: ProviderFactory<T>): T {
|
|
93
103
|
const instance = factory(this);
|
|
94
104
|
|
|
95
105
|
this.#instances.set(token, instance);
|
|
@@ -98,7 +108,7 @@ export class Injector {
|
|
|
98
108
|
* Only values that are objects are able to have associated injectors
|
|
99
109
|
*/
|
|
100
110
|
if (typeof instance === 'object' && instance !== null) {
|
|
101
|
-
const injector =
|
|
111
|
+
const injector = injectables.get(instance);
|
|
102
112
|
|
|
103
113
|
if (injector) {
|
|
104
114
|
/**
|
|
@@ -114,8 +124,12 @@ export class Injector {
|
|
|
114
124
|
* this ensures that services are initialized when the chain is settled
|
|
115
125
|
* this is required since the parent is set after the instance is constructed
|
|
116
126
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
const metadata = readMetadata(token as ConstructableToken<T>);
|
|
128
|
+
|
|
129
|
+
if (metadata) {
|
|
130
|
+
callLifecycle(instance, metadata.onCreated);
|
|
131
|
+
callLifecycle(instance, metadata.onInjected);
|
|
132
|
+
}
|
|
119
133
|
}
|
|
120
134
|
|
|
121
135
|
return instance;
|
|
@@ -136,12 +150,12 @@ export class Injector {
|
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
|
|
139
|
-
function callLifecycle(instance:
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
153
|
+
function callLifecycle(instance: object, methods?: unknown) {
|
|
154
|
+
if (Array.isArray(methods)) {
|
|
155
|
+
for (let cb of methods) {
|
|
156
|
+
if (typeof cb === 'function') {
|
|
157
|
+
cb.call(instance);
|
|
158
|
+
}
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
161
|
}
|