@joist/di 3.0.0-next.10 → 3.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 CHANGED
@@ -1,7 +1,6 @@
1
1
  # Di
2
2
 
3
- Dependency Injection in ~800 bytes. The Joist Dependency Injector is a small inversion of control (IOC) container that resolves dependencies lazyily.
4
- This means that it passes functions around and that dependencies are not initialized untill they are called.
3
+ Dependency Injection in ~800 bytes. This allows you to inject services into other class instances (including custom elements).
5
4
 
6
5
  #### Installation:
7
6
 
@@ -12,191 +11,43 @@ npm i @joist/di
12
11
  #### Example:
13
12
 
14
13
  ```TS
15
- import { Injector, Injected } from '@joist/di';
14
+ import { Injector, injectable, inject } from '@joist/di';
16
15
 
16
+ // Any class can be injected
17
17
  class FooService {
18
18
  sayHello() {
19
19
  return 'Hello From FooService';
20
20
  }
21
21
  }
22
22
 
23
+ // classes must be decorated with @injectable to use the inject function
24
+ @injectable
23
25
  class BarService {
24
- static inject = [FooService];
25
-
26
- constructor(public foo: Injected<FooService>) { }
26
+ foo = inject(FooService);
27
27
 
28
28
  sayHello() {
29
29
  return this.foo().sayHello();
30
30
  }
31
31
  }
32
32
 
33
- const app = new Injector();
34
-
35
- app.get(BarService).sayHello(); // Hello from BarService and Hello from FooService
36
- ```
37
-
38
- #### Override A Service:
39
-
40
- ```TS
41
- import { Injector, inject, Injected } from '@joist/di';
42
-
43
- class FooService {
44
- sayHello() {
45
- return 'Hello From FooService';
46
- }
47
- }
48
-
49
- class BarService {
50
- static inject = [FooService];
51
-
52
- constructor(public foo: Injected<FooService>) {}
53
-
54
- sayHello() {
55
- return 'Hello From BarService and ' + this.foo().sayHello();
56
- }
57
- }
58
-
59
- // Override FooService with an alternate implementation
60
- const app = new Injector([
61
- {
62
- provide: FooService,
63
- use: class extends FooService {
64
- sayHello() {
65
- return 'IT HAS BEEN OVERRIDEN'
66
- }
67
- }
68
- }
69
- ]);
70
-
71
- app.get(BarService).sayHello(); // Hello from BarService and IT HAS BEEN OVERRIDEN
72
- ```
73
-
74
- #### Root Service
75
-
76
- If you have nested injectors and you want to make sure the same instance is provided to all you can mark your class as a "service".
77
-
78
- ```TS
79
- class FooService {
80
- static service = true;
81
-
82
- sayHello() {
83
- return 'Hello From FooService';
84
- }
85
- }
86
- ```
87
-
88
- ## Custom Elements
89
-
90
- Joist DI was built with custom elements in mind. Custom elements are an example where you do not have direct control over how your classes are instantiated.
91
-
92
- Since the browser will be what initializes your custom elements we need to be able to tell the browser how to pass arguments.
93
-
94
- The `@injectable` decorator allows the Joist Dependency Injector to pass arguments to your custom element when instances of your element is created.
95
-
96
- #### Inject dependency into your custom element constructor
97
-
98
- ```TS
99
- import { injectable, Injected } from '@joist/di';
100
-
101
- class MyService {}
102
-
103
- @injectable
104
- class MyElement extends HTMLElement {
105
- static inject = [MyService];
106
-
107
- constructor(public myService: Injected<MyService>) {}
108
- }
109
-
110
- customElements.define('my-element', MyElement);
111
- ```
112
-
113
- #### Define global providers and overrides
114
-
115
- This allows your to override services for different environments or scenarios
116
-
117
- ```TS
118
- import { environment, injectable, Injected } from '@joist/di';
119
-
120
- class Config {
121
- apiUrl = 'http://localhost:4000/api/'
122
- }
123
-
124
- environment().providers.push({
125
- provide: Config,
126
- use: class {
127
- apiUrl = 'http://real-api/api/'
128
- }
129
- });
130
-
131
- @injectable
132
- class MyElement extends HTMLElement {
133
- static inject = [Config];
134
-
135
- constructor(config: Injected<Config>) {
136
- console.log(config().apiUrl); // http://real-api/api/
137
- }
138
- }
139
-
140
- customElements.define('my-element', MyElement);
141
- ```
142
-
143
- ## Context
144
-
145
- The Joist injector is hierarchical meaning that you can define context for just one part of the DOM tree.
146
-
147
- ### NOTE:
148
-
149
- When using context elements it is important that they are registered BEFORE your other elements.
150
- If child elements are upgraded before the context element they won't be able to find the context scope.
151
- If you plan on using context elements you will need to wait until the element has been attached so it can find any potential parent injectors.
152
-
153
- ```TS
154
- import { injectable, Injected } from '@joist/di';
155
-
156
- class Colors {
157
- primary = 'red';
158
- secodnary = 'green';
159
- }
160
-
161
- @injectable
162
- class ColorCtx extends HTMLElement {
163
- static providers = [
164
- {
165
- provide: Colors,
166
- use: class implements Colors {
167
- primary = 'orange';
168
- secondary = 'purple';
169
- },
170
- },
171
- ];
172
- }
173
-
174
33
  @injectable
175
- class MyElement extends HTMLElement {
176
- static inject = [Colors];
34
+ class BazService {
35
+ #bar = inject(BarService);
177
36
 
178
- constructor(public colors: Injected<Colors>) {
179
- super();
37
+ constructor() {
38
+ // will throw error
39
+ console.log(this.bar().sayHello())
180
40
  }
181
41
 
182
- connectedCallback() {
183
- // run in connected callback so your element can check its parents
184
- const { primary } = this.colors();
185
-
186
- this.style.background = primary;
42
+ // services cannot be accessed in the constructor.
43
+ // the onInject callback will be called when injectors have resolved
44
+ onInject() {
45
+ console.log(this.bar().sayHello())
187
46
  }
188
47
  }
189
48
 
190
- customElements.define('color-ctx', ColorCtx);
191
- customElements.define('my-element', MyElement);
192
- ```
193
49
 
194
- ```HTML
195
- <!-- Default Colors -->
196
- <my-element></my-element>
50
+ const app = new Injector();
197
51
 
198
- <!-- Special color ctx -->
199
- <color-ctx>
200
- <my-element></my-element>
201
- </color-ctx>
52
+ app.get(BazService);
202
53
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joist/di",
3
- "version": "3.0.0-next.10",
3
+ "version": "3.0.0-next.11",
4
4
  "main": "./target/lib.js",
5
5
  "module": "./target/lib.js",
6
6
  "exports": {
@@ -35,17 +35,10 @@
35
35
  "access": "public"
36
36
  },
37
37
  "scripts": {
38
- "prepare": "wireit",
39
38
  "test": "wireit",
40
39
  "build": "wireit"
41
40
  },
42
41
  "wireit": {
43
- "prepare": {
44
- "dependencies": [
45
- "build",
46
- "test"
47
- ]
48
- },
49
42
  "build": {
50
43
  "command": "tsc --build --pretty",
51
44
  "clean": "if-file-deleted",
@@ -5,6 +5,6 @@ export function environment() {
5
5
  }
6
6
  export function clearEnvironment() {
7
7
  rootInjector.providers = [];
8
- rootInjector.instances = new WeakMap();
8
+ rootInjector.clear();
9
9
  }
10
10
  //# sourceMappingURL=environment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/lib/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAC;AAEpC,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,YAAY,CAAC,SAAS,GAAG,EAAE,CAAC;IAC5B,YAAY,CAAC,SAAS,GAAG,IAAI,OAAO,EAAE,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/lib/environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,YAAY,GAAG,IAAI,QAAQ,EAAE,CAAC;AAEpC,MAAM,UAAU,WAAW;IACzB,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,YAAY,CAAC,SAAS,GAAG,EAAE,CAAC;IAC5B,YAAY,CAAC,KAAK,EAAE,CAAC;AACvB,CAAC"}
@@ -3,6 +3,7 @@ import { expect } from '@open-wc/testing';
3
3
  import { Injector } from './injector.js';
4
4
  import { environment, clearEnvironment } from './environment.js';
5
5
  import { injectable } from './injectable.js';
6
+ import { inject } from './inject.js';
6
7
  describe('environment', () => {
7
8
  afterEach(clearEnvironment);
8
9
  it('should create a global Injector instance', () => {
@@ -21,16 +22,9 @@ describe('environment', () => {
21
22
  static {
22
23
  __esDecorate(null, _classDescriptor = { value: this }, _classDecorators, { kind: "class", name: this.name }, null, _classExtraInitializers);
23
24
  MyElement = _classThis = _classDescriptor.value;
24
- }
25
- my;
26
- static inject = [MyService];
27
- constructor(my) {
28
- super();
29
- this.my = my;
30
- }
31
- static {
32
25
  __runInitializers(_classThis, _classExtraInitializers);
33
26
  }
27
+ my = inject(MyService);
34
28
  };
35
29
  return MyElement = _classThis;
36
30
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"environment.test.js","sourceRoot":"","sources":["../../src/lib/environment.test.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAY,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAE5B,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,SAAS;YACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;;YAIlB,SAAS;oCADd,UAAU;;;;gBACL,SAAS,QAAC,SAAQ,WAAW;;oBAAnC,4IAMC;oBANK,SAAS;;gBAGM,EAAE;gBAFrB,MAAM,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,CAAC;gBAE5B,YAAmB,EAAuB;oBACxC,KAAK,EAAE,CAAC;oBADS,OAAE,GAAF,EAAE,CAAqB;gBAE1C,CAAC;;oBALG,uDAAS;;;mBAAT,SAAS;;QAQf,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAE1C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAc,CAAC;QAExD,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"environment.test.js","sourceRoot":"","sources":["../../src/lib/environment.test.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAE5B,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,SAAS;YACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;;YAIlB,SAAS;oCADd,UAAU;;;;gBACL,SAAS,QAAC,SAAQ,WAAW;;oBAAnC,4IAEC;oBAFK,SAAS;oBAAT,uDAAS;;gBACb,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;;mBADnB,SAAS;;QAIf,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAE1C,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAc,CAAC;QAExD,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ProviderToken } from './provider.js';
2
+ import { Injectable, Injected } from './injector.js';
3
+ export declare function inject<This extends Injectable, T extends object>(token: ProviderToken<T>): Injected<T>;
@@ -0,0 +1,10 @@
1
+ export function inject(token) {
2
+ return function () {
3
+ if (this.injector$$ === undefined) {
4
+ const name = Object.getPrototypeOf(this.constructor).name;
5
+ 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 onInject callback method.`);
6
+ }
7
+ return this.injector$$.get(token);
8
+ };
9
+ }
10
+ //# sourceMappingURL=inject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/lib/inject.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,MAAM,CACpB,KAAuB;IAEvB,OAAO;QACL,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;YAE1D,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,6JAA6J,CACrK,CAAC;SACH;QAED,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,90 @@
1
+ import { __esDecorate, __runInitializers } from "tslib";
2
+ import { expect } from '@open-wc/testing';
3
+ import { inject } from './inject.js';
4
+ import { injectable } from './injectable.js';
5
+ import { Injector } from './injector.js';
6
+ describe('inject', () => {
7
+ it('should work', () => {
8
+ class HelloService {
9
+ }
10
+ let HelloWorld = (() => {
11
+ let _classDecorators = [injectable];
12
+ let _classDescriptor;
13
+ let _classExtraInitializers = [];
14
+ let _classThis;
15
+ var HelloWorld = class extends HTMLElement {
16
+ static {
17
+ __esDecorate(null, _classDescriptor = { value: this }, _classDecorators, { kind: "class", name: this.name }, null, _classExtraInitializers);
18
+ HelloWorld = _classThis = _classDescriptor.value;
19
+ __runInitializers(_classThis, _classExtraInitializers);
20
+ }
21
+ hello = inject(HelloService);
22
+ };
23
+ return HelloWorld = _classThis;
24
+ })();
25
+ customElements.define('inject-1', HelloWorld);
26
+ expect(new HelloWorld().hello()).to.be.instanceOf(HelloService);
27
+ });
28
+ it('should throw error if called in constructor', () => {
29
+ class FooService {
30
+ value = '1';
31
+ }
32
+ let BarService = (() => {
33
+ let _classDecorators_1 = [injectable];
34
+ let _classDescriptor_1;
35
+ let _classExtraInitializers_1 = [];
36
+ let _classThis_1;
37
+ var BarService = class {
38
+ static {
39
+ __esDecorate(null, _classDescriptor_1 = { value: this }, _classDecorators_1, { kind: "class", name: this.name }, null, _classExtraInitializers_1);
40
+ BarService = _classThis_1 = _classDescriptor_1.value;
41
+ __runInitializers(_classThis_1, _classExtraInitializers_1);
42
+ }
43
+ foo = inject(FooService);
44
+ constructor() {
45
+ this.foo();
46
+ }
47
+ };
48
+ return BarService = _classThis_1;
49
+ })();
50
+ const parent = new Injector();
51
+ try {
52
+ parent.get(BarService);
53
+ throw new Error('Should not succeed');
54
+ }
55
+ catch (err) {
56
+ const error = err;
57
+ expect(error.message).to.equal(`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 onInject callback method.`);
58
+ }
59
+ });
60
+ it('should use the calling injector as parent', () => {
61
+ class FooService {
62
+ value = '1';
63
+ }
64
+ let BarService = (() => {
65
+ let _classDecorators_2 = [injectable];
66
+ let _classDescriptor_2;
67
+ let _classExtraInitializers_2 = [];
68
+ let _classThis_2;
69
+ var BarService = class {
70
+ static {
71
+ __esDecorate(null, _classDescriptor_2 = { value: this }, _classDecorators_2, { kind: "class", name: this.name }, null, _classExtraInitializers_2);
72
+ BarService = _classThis_2 = _classDescriptor_2.value;
73
+ __runInitializers(_classThis_2, _classExtraInitializers_2);
74
+ }
75
+ foo = inject(FooService);
76
+ };
77
+ return BarService = _classThis_2;
78
+ })();
79
+ const parent = new Injector([
80
+ {
81
+ provide: FooService,
82
+ use: class extends FooService {
83
+ value = '100';
84
+ },
85
+ },
86
+ ]);
87
+ expect(parent.get(BarService).foo().value).to.equal('100');
88
+ });
89
+ });
90
+ //# sourceMappingURL=inject.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject.test.js","sourceRoot":"","sources":["../../src/lib/inject.test.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,aAAa,EAAE,GAAG,EAAE;QACrB,MAAM,YAAY;SAAG;YAGf,UAAU;oCADf,UAAU;;;;gBACL,UAAU,QAAC,SAAQ,WAAW;;oBAApC,4IAEC;oBAFK,UAAU;oBAAV,uDAAU;;gBACd,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;;mBADzB,UAAU;;QAIhB,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAE9C,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,UAAU;YACd,KAAK,GAAG,GAAG,CAAC;SACb;YAGK,UAAU;sCADf,UAAU;;;;gBACL,UAAU;;oBAAhB,kJAMC;oBANK,UAAU;oBAAV,2DAAU;;gBACd,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;gBAEzB;oBACE,IAAI,CAAC,GAAG,EAAE,CAAC;gBACb,CAAC;;mBALG,UAAU;;QAQhB,MAAM,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAE9B,IAAI;YACF,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAEvB,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;SACvC;QAAC,OAAO,GAAG,EAAE;YACZ,MAAM,KAAK,GAAG,GAAY,CAAC;YAE3B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,CAC5B,uKAAuK,CACxK,CAAC;SACH;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,UAAU;YACd,KAAK,GAAG,GAAG,CAAC;SACb;YAGK,UAAU;sCADf,UAAU;;;;gBACL,UAAU;;oBAAhB,kJAEC;oBAFK,UAAU;oBAAV,2DAAU;;gBACd,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;;mBADrB,UAAU;;QAIhB,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;YAC1B;gBACE,OAAO,EAAE,UAAU;gBACnB,GAAG,EAAE,KAAM,SAAQ,UAAU;oBAC3B,KAAK,GAAG,KAAK,CAAC;iBACf;aACF;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}