@joist/di 4.0.0-next.1 → 4.0.0-next.3
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 +15 -27
- package/package.json +1 -1
- package/src/lib/dom-injector.test.ts +4 -4
- package/src/lib/dom-injector.ts +3 -4
- package/src/lib/inject.test.ts +5 -5
- package/src/lib/inject.ts +5 -6
- package/src/lib/injectable.test.ts +27 -29
- package/src/lib/injectable.ts +40 -36
- package/src/lib/injector.test-node.ts +6 -6
- package/src/lib/injector.test.ts +6 -6
- package/src/lib/injector.ts +7 -7
- package/src/lib/lifecycle.test.ts +4 -4
- package/src/lib/provider.ts +12 -4
- package/target/lib/dom-injector.js +3 -4
- package/target/lib/dom-injector.js.map +1 -1
- package/target/lib/dom-injector.test.js +4 -4
- 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 +5 -5
- package/target/lib/inject.test.js.map +1 -1
- package/target/lib/injectable.d.ts +6 -5
- package/target/lib/injectable.js +32 -32
- package/target/lib/injectable.js.map +1 -1
- package/target/lib/injectable.test.js +54 -26
- package/target/lib/injectable.test.js.map +1 -1
- package/target/lib/injector.d.ts +1 -1
- package/target/lib/injector.js +2 -5
- package/target/lib/injector.js.map +1 -1
- package/target/lib/injector.test-node.js +6 -6
- package/target/lib/injector.test-node.js.map +1 -1
- package/target/lib/injector.test.js +6 -6
- package/target/lib/injector.test.js.map +1 -1
- package/target/lib/lifecycle.test.js +4 -4
- package/target/lib/lifecycle.test.js.map +1 -1
- package/target/lib/provider.d.ts +3 -2
- package/target/lib/provider.js +10 -4
- package/target/lib/provider.js.map +1 -1
- package/src/lib/injectable-map.test.ts +0 -18
- package/src/lib/injectable-map.ts +0 -3
- 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.d.ts +0 -1
- package/target/lib/injectable-map.test.js +0 -15
- package/target/lib/injectable-map.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ Singleton services are great but the real benefit can be seen when passing insta
|
|
|
53
53
|
`inject()` returns a function that will then return an instance of the requested service. This means that services are only created when they are needed and not when the class is constructed.
|
|
54
54
|
|
|
55
55
|
```ts
|
|
56
|
-
@injectable
|
|
56
|
+
@injectable()
|
|
57
57
|
class App {
|
|
58
58
|
#counter = inject(Counter);
|
|
59
59
|
|
|
@@ -80,7 +80,7 @@ class HttpService {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
@injectable
|
|
83
|
+
@injectable()
|
|
84
84
|
class ApiService {
|
|
85
85
|
#http = inject(HttpService);
|
|
86
86
|
|
|
@@ -114,7 +114,7 @@ test('should return json', async () => {
|
|
|
114
114
|
|
|
115
115
|
### Service level providers
|
|
116
116
|
|
|
117
|
-
Under the hood, each service decorated with `@injectable` creates its own injector. This means that it is possible to defined providers from that level down.
|
|
117
|
+
Under the hood, each service decorated with `@injectable()` creates its own injector. This means that it is possible to defined providers from that level down.
|
|
118
118
|
|
|
119
119
|
The below example will use this particular instance of Logger as wall as any other services injected into this service.
|
|
120
120
|
|
|
@@ -129,10 +129,10 @@ class ConsoleLogger implements Logger {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
@injectable
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
132
|
+
@injectable({
|
|
133
|
+
providers: [{ provide: Logger, use: ConsoleLogger }]
|
|
134
|
+
})
|
|
135
|
+
class MyService {}
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
### Factories
|
|
@@ -286,7 +286,7 @@ class Colors {
|
|
|
286
286
|
secodnary = 'green';
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
@injectable
|
|
289
|
+
@injectable()
|
|
290
290
|
class MyElement extends HTMLElement {
|
|
291
291
|
#colors = inject(Colors);
|
|
292
292
|
|
|
@@ -314,21 +314,20 @@ class Colors {
|
|
|
314
314
|
secodnary = 'green';
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
-
@injectable
|
|
318
|
-
|
|
319
|
-
// services can be scoped to a particular injectable
|
|
320
|
-
static providers = [
|
|
317
|
+
@injectable({
|
|
318
|
+
providers: [
|
|
321
319
|
{
|
|
322
320
|
provide: Colors,
|
|
323
321
|
use: class implements Colors {
|
|
324
322
|
primary = 'orange';
|
|
325
323
|
secondary = 'purple';
|
|
326
|
-
}
|
|
327
|
-
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
328
326
|
]
|
|
329
|
-
}
|
|
327
|
+
})
|
|
328
|
+
class ColorCtx extends HTMLElement {}
|
|
330
329
|
|
|
331
|
-
@injectable
|
|
330
|
+
@injectable()
|
|
332
331
|
class MyElement extends HTMLElement {
|
|
333
332
|
#colors = inject(Colors);
|
|
334
333
|
|
|
@@ -353,14 +352,3 @@ customElements.define('my-element', MyElement);
|
|
|
353
352
|
<my-element></my-element>
|
|
354
353
|
</color-ctx>
|
|
355
354
|
```
|
|
356
|
-
|
|
357
|
-
### Environment
|
|
358
|
-
|
|
359
|
-
When using @joist/di with custom elements a default root injector is created dubbed 'environment'. This is the injector that all other injectors will eventually stop at.
|
|
360
|
-
If you need to define something in this environment you can do so with the `defineEnvironment` method.
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
import { defineEnvironment } from '@joist/di';
|
|
364
|
-
|
|
365
|
-
defineEnvironment([{ provide: MyService, use: SomeOtherService }]);
|
|
366
|
-
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect } from '@open-wc/testing';
|
|
2
2
|
|
|
3
3
|
import { DOMInjector } from './dom-injector.js';
|
|
4
|
-
import {
|
|
4
|
+
import { Injectables } from './injector.js';
|
|
5
5
|
|
|
6
6
|
describe('DOMInjector', () => {
|
|
7
7
|
it('should attach an injector to a dom element', () => {
|
|
@@ -10,7 +10,7 @@ describe('DOMInjector', () => {
|
|
|
10
10
|
|
|
11
11
|
app.attach(root);
|
|
12
12
|
|
|
13
|
-
const injector =
|
|
13
|
+
const injector = Injectables.get(root);
|
|
14
14
|
|
|
15
15
|
expect(injector).to.equal(app);
|
|
16
16
|
});
|
|
@@ -21,10 +21,10 @@ describe('DOMInjector', () => {
|
|
|
21
21
|
|
|
22
22
|
app.attach(root);
|
|
23
23
|
|
|
24
|
-
expect(
|
|
24
|
+
expect(Injectables.get(root)).to.equal(app);
|
|
25
25
|
|
|
26
26
|
app.detach(root);
|
|
27
27
|
|
|
28
|
-
expect(
|
|
28
|
+
expect(Injectables.get(root)).to.equal(undefined);
|
|
29
29
|
});
|
|
30
30
|
});
|
package/src/lib/dom-injector.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Injector } from './injector.js';
|
|
1
|
+
import { Injectables, Injector } from './injector.js';
|
|
3
2
|
|
|
4
3
|
export class DOMInjector extends Injector {
|
|
5
4
|
attach(root: HTMLElement) {
|
|
6
|
-
|
|
5
|
+
Injectables.set(root, this);
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
detach(root: HTMLElement) {
|
|
10
|
-
|
|
9
|
+
Injectables.delete(root);
|
|
11
10
|
}
|
|
12
11
|
}
|
package/src/lib/inject.test.ts
CHANGED
|
@@ -9,7 +9,7 @@ describe('inject', () => {
|
|
|
9
9
|
it('should work', () => {
|
|
10
10
|
class HelloService {}
|
|
11
11
|
|
|
12
|
-
@injectable
|
|
12
|
+
@injectable()
|
|
13
13
|
class HelloWorld extends HTMLElement {
|
|
14
14
|
hello = inject(HelloService);
|
|
15
15
|
}
|
|
@@ -24,7 +24,7 @@ describe('inject', () => {
|
|
|
24
24
|
value = '1';
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
@injectable
|
|
27
|
+
@injectable()
|
|
28
28
|
class BarService {
|
|
29
29
|
foo = inject(FooService);
|
|
30
30
|
|
|
@@ -43,7 +43,7 @@ describe('inject', () => {
|
|
|
43
43
|
const error = err as Error;
|
|
44
44
|
|
|
45
45
|
expect(error.message).to.equal(
|
|
46
|
-
`BarService is either not injectable or a service is being called in the constructor. \n Either add the @injectable to your class or use the [LifeCycle.onInject] callback method.`
|
|
46
|
+
`BarService is either not injectable or a service is being called in the constructor. \n Either add the @injectable() to your class or use the [LifeCycle.onInject] callback method.`
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
@@ -53,7 +53,7 @@ describe('inject', () => {
|
|
|
53
53
|
value = '1';
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
@injectable
|
|
56
|
+
@injectable()
|
|
57
57
|
class BarService {
|
|
58
58
|
foo = inject(FooService);
|
|
59
59
|
}
|
|
@@ -73,7 +73,7 @@ describe('inject', () => {
|
|
|
73
73
|
it('should inject a static token', () => {
|
|
74
74
|
const TOKEN = new StaticToken('test', () => 'Hello World');
|
|
75
75
|
|
|
76
|
-
@injectable
|
|
76
|
+
@injectable()
|
|
77
77
|
class HelloWorld {
|
|
78
78
|
hello = inject(TOKEN);
|
|
79
79
|
}
|
package/src/lib/inject.ts
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { InjectionToken } from './provider.js';
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { Injectables } from './injector.js';
|
|
3
4
|
|
|
4
5
|
export type Injected<T> = () => T;
|
|
5
6
|
|
|
6
|
-
export function inject<This extends object, T>(
|
|
7
|
-
token: InjectionToken<T>
|
|
8
|
-
): Injected<T> {
|
|
7
|
+
export function inject<This extends object, T>(token: InjectionToken<T>): Injected<T> {
|
|
9
8
|
return function (this: This) {
|
|
10
|
-
const injector =
|
|
9
|
+
const injector = Injectables.get(this);
|
|
11
10
|
|
|
12
11
|
if (injector === undefined) {
|
|
13
12
|
const name = Object.getPrototypeOf(this.constructor).name;
|
|
14
13
|
|
|
15
14
|
throw new Error(
|
|
16
|
-
`${name} is either not injectable or a service is being called in the constructor. \n Either add the @injectable to your class or use the [LifeCycle.onInject] callback method.`
|
|
15
|
+
`${name} is either not injectable or a service is being called in the constructor. \n Either add the @injectable() to your class or use the [LifeCycle.onInject] callback method.`
|
|
17
16
|
);
|
|
18
17
|
}
|
|
19
18
|
|
|
@@ -8,7 +8,7 @@ describe('@injectable()', () => {
|
|
|
8
8
|
class Foo {}
|
|
9
9
|
class Bar {}
|
|
10
10
|
|
|
11
|
-
@injectable
|
|
11
|
+
@injectable()
|
|
12
12
|
class MyElement extends HTMLElement {
|
|
13
13
|
foo = inject(Foo);
|
|
14
14
|
bar = inject(Bar);
|
|
@@ -26,13 +26,12 @@ describe('@injectable()', () => {
|
|
|
26
26
|
|
|
27
27
|
class Bar extends Foo {}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
);
|
|
29
|
+
@injectable({
|
|
30
|
+
providers: [{ provide: Foo, use: Bar }]
|
|
31
|
+
})
|
|
32
|
+
class MyElement {
|
|
33
|
+
foo = inject(Foo);
|
|
34
|
+
}
|
|
36
35
|
|
|
37
36
|
const el = new MyElement();
|
|
38
37
|
|
|
@@ -40,26 +39,25 @@ describe('@injectable()', () => {
|
|
|
40
39
|
});
|
|
41
40
|
|
|
42
41
|
it('should handle parent HTML Injectors', async () => {
|
|
43
|
-
@injectable
|
|
42
|
+
@injectable()
|
|
44
43
|
class A {}
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
);
|
|
45
|
+
@injectable()
|
|
46
|
+
class B {
|
|
47
|
+
a = inject(A);
|
|
48
|
+
}
|
|
51
49
|
|
|
52
50
|
class AltA implements A {}
|
|
53
51
|
|
|
54
|
-
@injectable
|
|
55
|
-
|
|
56
|
-
static providers = [
|
|
52
|
+
@injectable({
|
|
53
|
+
providers: [
|
|
57
54
|
{ provide: B, use: B },
|
|
58
55
|
{ provide: A, use: AltA }
|
|
59
|
-
]
|
|
60
|
-
}
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
class Parent extends HTMLElement {}
|
|
61
59
|
|
|
62
|
-
@injectable
|
|
60
|
+
@injectable()
|
|
63
61
|
class Child extends HTMLElement {
|
|
64
62
|
b = inject(B);
|
|
65
63
|
}
|
|
@@ -82,17 +80,17 @@ describe('@injectable()', () => {
|
|
|
82
80
|
class A {}
|
|
83
81
|
class AltA implements A {}
|
|
84
82
|
|
|
85
|
-
@injectable
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
83
|
+
@injectable({
|
|
84
|
+
providers: [{ provide: A, use: A }]
|
|
85
|
+
})
|
|
86
|
+
class Ctx1 extends HTMLElement {}
|
|
89
87
|
|
|
90
|
-
@injectable
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
88
|
+
@injectable({
|
|
89
|
+
providers: [{ provide: A, use: AltA }]
|
|
90
|
+
})
|
|
91
|
+
class Ctx2 extends HTMLElement {}
|
|
94
92
|
|
|
95
|
-
@injectable
|
|
93
|
+
@injectable()
|
|
96
94
|
class Child extends HTMLElement {
|
|
97
95
|
a = inject(A);
|
|
98
96
|
}
|
package/src/lib/injectable.ts
CHANGED
|
@@ -1,52 +1,56 @@
|
|
|
1
|
-
import { ConstructableToken } from './provider.js';
|
|
2
|
-
import { Injector } from './injector.js';
|
|
3
|
-
import { InjectableMap } from './injectable-map.js';
|
|
1
|
+
import { ConstructableToken, Provider } from './provider.js';
|
|
2
|
+
import { Injectables, Injector } from './injector.js';
|
|
4
3
|
|
|
5
|
-
export
|
|
4
|
+
export interface InjectableOpts {
|
|
5
|
+
providers: Provider<unknown>[];
|
|
6
|
+
}
|
|
6
7
|
|
|
7
|
-
export function injectable
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
export function injectable(opts?: InjectableOpts) {
|
|
9
|
+
return function injectableDecorator<T extends ConstructableToken<any>>(Base: T, _?: unknown) {
|
|
10
|
+
return class InjectableNode extends Base {
|
|
11
|
+
constructor(..._: any[]) {
|
|
12
|
+
super();
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
INJECTABLE_MAP.set(this, injector);
|
|
14
|
+
// Define a new Injector and assiciate it with this instance of the service
|
|
15
|
+
const injector = new Injector(opts?.providers);
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
// this will find and attach parent injectors
|
|
18
|
-
if ('HTMLElement' in globalThis && this instanceof HTMLElement) {
|
|
19
|
-
this.addEventListener('finddiroot', (e) => {
|
|
20
|
-
const parentInjector = findInjectorRoot(e);
|
|
17
|
+
Injectables.set(this, injector);
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
// If the current injectable instance is a HTMLElement preform additional startup logic
|
|
20
|
+
// this will find and attach parent injectors
|
|
21
|
+
if ('HTMLElement' in globalThis && this instanceof HTMLElement) {
|
|
22
|
+
this.addEventListener('finddiroot', (e) => {
|
|
23
|
+
const parentInjector = findInjectorRoot(e);
|
|
24
|
+
|
|
25
|
+
if (parentInjector !== null) {
|
|
26
|
+
injector.setParent(parentInjector);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
26
30
|
}
|
|
27
|
-
}
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
connectedCallback() {
|
|
33
|
+
if ('HTMLElement' in globalThis && this instanceof HTMLElement) {
|
|
34
|
+
this.dispatchEvent(new Event('finddiroot'));
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
if (super.connectedCallback) {
|
|
37
|
+
super.connectedCallback();
|
|
38
|
+
}
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
|
-
}
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
disconnectedCallback() {
|
|
43
|
+
const injector = Injectables.get(this);
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (injector) {
|
|
46
|
+
injector.setParent(undefined);
|
|
47
|
+
}
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
if (super.disconnectedCallback) {
|
|
50
|
+
super.disconnectedCallback();
|
|
51
|
+
}
|
|
48
52
|
}
|
|
49
|
-
}
|
|
53
|
+
};
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -58,7 +62,7 @@ function findInjectorRoot(e: Event): Injector | null {
|
|
|
58
62
|
for (let i = 1; i < path.length; i++) {
|
|
59
63
|
const part = path[i];
|
|
60
64
|
|
|
61
|
-
const injector =
|
|
65
|
+
const injector = Injectables.get(part);
|
|
62
66
|
|
|
63
67
|
if (injector) {
|
|
64
68
|
return injector;
|
|
@@ -20,7 +20,7 @@ test('should inject providers in the correct order', () => {
|
|
|
20
20
|
class A {}
|
|
21
21
|
class B {}
|
|
22
22
|
|
|
23
|
-
@injectable
|
|
23
|
+
@injectable()
|
|
24
24
|
class MyService {
|
|
25
25
|
a = inject(A);
|
|
26
26
|
b = inject(B);
|
|
@@ -36,22 +36,22 @@ test('should inject providers in the correct order', () => {
|
|
|
36
36
|
test('should create a new instance of a provider that has a full dep tree', () => {
|
|
37
37
|
class A {}
|
|
38
38
|
|
|
39
|
-
@injectable
|
|
39
|
+
@injectable()
|
|
40
40
|
class B {
|
|
41
41
|
a = inject(A);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
@injectable
|
|
44
|
+
@injectable()
|
|
45
45
|
class C {
|
|
46
46
|
b = inject(B);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
@injectable
|
|
49
|
+
@injectable()
|
|
50
50
|
class D {
|
|
51
51
|
c = inject(C);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
@injectable
|
|
54
|
+
@injectable()
|
|
55
55
|
class E {
|
|
56
56
|
d = inject(D);
|
|
57
57
|
}
|
|
@@ -65,7 +65,7 @@ test('should create a new instance of a provider that has a full dep tree', () =
|
|
|
65
65
|
test('should override a provider if explicitly instructed', () => {
|
|
66
66
|
class A {}
|
|
67
67
|
|
|
68
|
-
@injectable
|
|
68
|
+
@injectable()
|
|
69
69
|
class B {
|
|
70
70
|
a = inject(A);
|
|
71
71
|
}
|
package/src/lib/injector.test.ts
CHANGED
|
@@ -19,7 +19,7 @@ describe('Injector', () => {
|
|
|
19
19
|
class A {}
|
|
20
20
|
class B {}
|
|
21
21
|
|
|
22
|
-
@injectable
|
|
22
|
+
@injectable()
|
|
23
23
|
class MyService {
|
|
24
24
|
a = inject(A);
|
|
25
25
|
b = inject(B);
|
|
@@ -35,22 +35,22 @@ describe('Injector', () => {
|
|
|
35
35
|
it('should create a new instance of a provider that has a full dep tree', () => {
|
|
36
36
|
class A {}
|
|
37
37
|
|
|
38
|
-
@injectable
|
|
38
|
+
@injectable()
|
|
39
39
|
class B {
|
|
40
40
|
a = inject(A);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
@injectable
|
|
43
|
+
@injectable()
|
|
44
44
|
class C {
|
|
45
45
|
b = inject(B);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
@injectable
|
|
48
|
+
@injectable()
|
|
49
49
|
class D {
|
|
50
50
|
c = inject(C);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
@injectable
|
|
53
|
+
@injectable()
|
|
54
54
|
class E {
|
|
55
55
|
d = inject(D);
|
|
56
56
|
}
|
|
@@ -64,7 +64,7 @@ describe('Injector', () => {
|
|
|
64
64
|
it('should override a provider if explicitly instructed', () => {
|
|
65
65
|
class A {}
|
|
66
66
|
|
|
67
|
-
@injectable
|
|
67
|
+
@injectable()
|
|
68
68
|
class B {
|
|
69
69
|
a = inject(A);
|
|
70
70
|
}
|
package/src/lib/injector.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { INJECTABLE_MAP } from './injectable.js';
|
|
2
1
|
import { LifeCycle } from './lifecycle.js';
|
|
3
2
|
import { InjectionToken, Provider, StaticToken } from './provider.js';
|
|
4
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Keeps track of all Injectable services and their Injector
|
|
6
|
+
*/
|
|
7
|
+
export const Injectables = new WeakMap<object, Injector>();
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* Injectors create and store instances of services.
|
|
7
11
|
* A service is any constructable class.
|
|
@@ -31,12 +35,8 @@ export class Injector {
|
|
|
31
35
|
this.providers = providers;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
inject<T>(token: InjectionToken<T>): T {
|
|
35
|
-
return this.get(token);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
38
|
// resolves and retuns and instance of the requested service
|
|
39
|
-
|
|
39
|
+
inject<T>(token: InjectionToken<T>): T {
|
|
40
40
|
// check for a local instance
|
|
41
41
|
if (this.#instances.has(token)) {
|
|
42
42
|
const instance = this.#instances.get(token)!;
|
|
@@ -98,7 +98,7 @@ export class Injector {
|
|
|
98
98
|
* Only values that are objects are able to have associated injectors
|
|
99
99
|
*/
|
|
100
100
|
if (typeof instance === 'object' && instance !== null) {
|
|
101
|
-
const injector =
|
|
101
|
+
const injector = Injectables.get(instance);
|
|
102
102
|
|
|
103
103
|
if (injector) {
|
|
104
104
|
/**
|
|
@@ -14,7 +14,7 @@ describe('LifeCycle', () => {
|
|
|
14
14
|
onInject: 0
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
@injectable
|
|
17
|
+
@injectable()
|
|
18
18
|
class MyService {
|
|
19
19
|
[LifeCycle.onInit]() {
|
|
20
20
|
res.onInit++;
|
|
@@ -41,7 +41,7 @@ describe('LifeCycle', () => {
|
|
|
41
41
|
onInject: 0
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
@injectable
|
|
44
|
+
@injectable()
|
|
45
45
|
class MyService {
|
|
46
46
|
[LifeCycle.onInit]() {
|
|
47
47
|
res.onInit++;
|
|
@@ -69,7 +69,7 @@ describe('LifeCycle', () => {
|
|
|
69
69
|
onInject: 0
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
-
@injectable
|
|
72
|
+
@injectable()
|
|
73
73
|
class MyService {
|
|
74
74
|
[LifeCycle.onInit]() {
|
|
75
75
|
res.onInit++;
|
|
@@ -80,7 +80,7 @@ describe('LifeCycle', () => {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
@injectable
|
|
83
|
+
@injectable()
|
|
84
84
|
class MyApp {
|
|
85
85
|
service = inject(MyService);
|
|
86
86
|
}
|
package/src/lib/provider.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { Injector } from './injector.js';
|
|
2
2
|
|
|
3
3
|
export class StaticToken<T> {
|
|
4
|
-
name;
|
|
5
|
-
factory;
|
|
4
|
+
#name;
|
|
5
|
+
#factory;
|
|
6
|
+
|
|
7
|
+
get name() {
|
|
8
|
+
return this.#name;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get factory() {
|
|
12
|
+
return this.#factory;
|
|
13
|
+
}
|
|
6
14
|
|
|
7
15
|
constructor(name: string, factory?: () => T) {
|
|
8
|
-
this
|
|
9
|
-
this
|
|
16
|
+
this.#name = name;
|
|
17
|
+
this.#factory = factory;
|
|
10
18
|
}
|
|
11
19
|
}
|
|
12
20
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Injector } from './injector.js';
|
|
1
|
+
import { Injectables, Injector } from './injector.js';
|
|
3
2
|
export class DOMInjector extends Injector {
|
|
4
3
|
attach(root) {
|
|
5
|
-
|
|
4
|
+
Injectables.set(root, this);
|
|
6
5
|
}
|
|
7
6
|
detach(root) {
|
|
8
|
-
|
|
7
|
+
Injectables.delete(root);
|
|
9
8
|
}
|
|
10
9
|
}
|
|
11
10
|
//# sourceMappingURL=dom-injector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-injector.js","sourceRoot":"","sources":["../../src/lib/dom-injector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dom-injector.js","sourceRoot":"","sources":["../../src/lib/dom-injector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,OAAO,WAAY,SAAQ,QAAQ;IACvC,MAAM,CAAC,IAAiB;QACtB,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,IAAiB;QACtB,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { expect } from '@open-wc/testing';
|
|
2
2
|
import { DOMInjector } from './dom-injector.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Injectables } from './injector.js';
|
|
4
4
|
describe('DOMInjector', () => {
|
|
5
5
|
it('should attach an injector to a dom element', () => {
|
|
6
6
|
const root = document.createElement('div');
|
|
7
7
|
const app = new DOMInjector();
|
|
8
8
|
app.attach(root);
|
|
9
|
-
const injector =
|
|
9
|
+
const injector = Injectables.get(root);
|
|
10
10
|
expect(injector).to.equal(app);
|
|
11
11
|
});
|
|
12
12
|
it('should remove an injector associated with a dom element', () => {
|
|
13
13
|
const root = document.createElement('div');
|
|
14
14
|
const app = new DOMInjector();
|
|
15
15
|
app.attach(root);
|
|
16
|
-
expect(
|
|
16
|
+
expect(Injectables.get(root)).to.equal(app);
|
|
17
17
|
app.detach(root);
|
|
18
|
-
expect(
|
|
18
|
+
expect(Injectables.get(root)).to.equal(undefined);
|
|
19
19
|
});
|
|
20
20
|
});
|
|
21
21
|
//# sourceMappingURL=dom-injector.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-injector.test.js","sourceRoot":"","sources":["../../src/lib/dom-injector.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dom-injector.test.js","sourceRoot":"","sources":["../../src/lib/dom-injector.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAE9B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEjB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEvC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;QAE9B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEjB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE5C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEjB,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/target/lib/inject.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Injectables } from './injector.js';
|
|
2
2
|
export function inject(token) {
|
|
3
3
|
return function () {
|
|
4
|
-
const injector =
|
|
4
|
+
const injector = Injectables.get(this);
|
|
5
5
|
if (injector === undefined) {
|
|
6
6
|
const name = Object.getPrototypeOf(this.constructor).name;
|
|
7
|
-
throw new Error(`${name} is either not injectable or a service is being called in the constructor. \n Either add the @injectable to your class or use the [LifeCycle.onInject] callback method.`);
|
|
7
|
+
throw new Error(`${name} is either not injectable or a service is being called in the constructor. \n Either add the @injectable() to your class or use the [LifeCycle.onInject] callback method.`);
|
|
8
8
|
}
|
|
9
9
|
return injector.inject(token);
|
|
10
10
|
};
|