first-di 1.0.19 → 2.0.1

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
@@ -3,35 +3,64 @@ First DI
3
3
 
4
4
  Easy dependency injection for typescript applications
5
5
 
6
- Description:
6
+ Installation
7
7
  ------
8
- - For working this library needed Metadata Reflection API. If your platform (browser/nodejs) don't support it you must use polifyll. Example: [reflect-metadata](https://www.npmjs.com/package/reflect-metadata).
9
- - For working reflection should be enabled the option emitDecoratorMetadata and experimentalDecorators in tsconfig file.
10
- - For generate reflection by typescript need to create any decorator or use @reflection decorator from this library.
11
- - Lazy Loading for resolve dependency. Each dependency will created only after request property with @autowired decorator.
8
+
9
+ For the latest stable version:
10
+
11
+ ```Bash
12
+ npm i first-di
13
+ ```
14
+
15
+ Features
16
+ ------
17
+
18
+ - Easy and powerful dependency injection for any typescript application.
19
+ - 2 modes of work. Optional DI - for most apps. Classic DI - for advanced apps.
20
+ - Support for multiple scopes.
21
+ - Supports multiple life cycles.
12
22
  - Dependency Free. Dependency used only for development.
13
23
 
14
- Using in Easy mode:
24
+ Setup
15
25
  ------
16
- Simply write code as you used to and use @autowired() decorator to implement dependencies. And for override dependencies just use method override.
26
+ Install [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) package and import in root typescript file. This package is needed to support reflection and is a mandatory requirement of Typescript.
27
+
28
+ In tsconfig.json enable compiler options:
29
+
30
+ ```Json
31
+ {
32
+ "compilerOptions": {
33
+ ...
34
+ "emitDecoratorMetadata": true,
35
+ "experimentalDecorators": true,
36
+ ...
37
+ }
38
+ }
39
+
40
+ ```
41
+
42
+ Using in Optional DI mode
43
+ ------
44
+
45
+ Just write classes and inject dependencies through class constructors. When the 'resolve' function is called, all dependencies will be resolved.
17
46
 
18
47
  ```typescript
19
- import { autowired, override, reflection } from "first-di";
48
+ import { resolve, override, reflection } from "first-di";
20
49
 
21
- @reflection // typescript will generate reflection metadata
22
- class ProdRepository { // default implementation
50
+ @reflection // Typescript will generate reflection metadata
51
+ class ProdRepository { // Default implementation
23
52
 
24
- public async getData(): Promise<string> {
25
- return await Promise.resolve("production");
53
+ public async getData (): Promise<string> {
54
+ return Promise.resolve("production");
26
55
  }
27
56
 
28
57
  }
29
58
 
30
59
  @reflection
31
- class MockRepository { // mock implementation with same interface
60
+ class MockRepository implements ProdRepository { // Mock implementation with same interface
32
61
 
33
- public async getData(): Promise<string> {
34
- return await Promise.resolve("mock");
62
+ public async getData (): Promise<string> {
63
+ return Promise.resolve("mock");
35
64
  }
36
65
 
37
66
  }
@@ -39,33 +68,38 @@ class MockRepository { // mock implementation with same interface
39
68
  @reflection
40
69
  class ProdService {
41
70
 
42
- constructor(private readonly prodRepository: ProdRepository) { }
71
+ public constructor (
72
+ private readonly prodRepository: ProdRepository
73
+ ) { }
43
74
 
44
- public async getData(): Promise<string> {
45
- return await this.prodRepository.getData();
75
+ public async getData (): Promise<string> {
76
+ return this.prodRepository.getData();
46
77
  }
47
78
 
48
79
  }
49
80
 
50
- class ProdController {
51
-
52
- @autowired() // inject dependency
53
- private readonly prodService!: ProdService;
81
+ @reflection
82
+ class ProdStore {
54
83
 
55
- // constructor use library, don't use him for inject
84
+ public constructor (
85
+ // Inject dependency
86
+ private readonly prodService: ProdService
87
+ ) {
88
+ // Other logic here
89
+ }
56
90
 
57
- public async getData(): Promise<string> {
58
- return await this.prodService.getData();
91
+ public async getData (): Promise<string> {
92
+ return this.prodService.getData();
59
93
  }
60
94
 
61
95
  }
62
96
 
63
- if (process.env.NODE_ENV === "test") { // override in test environment
97
+ if (process.env.NODE_ENV === "test") { // Override in test environment
64
98
  override(ProdRepository, MockRepository);
65
99
  }
66
100
 
67
- const controllerInstance = new ProdController(); // create intance by framework
68
- const data = await controllerInstance.getData();
101
+ const store = resolve(ProdStore); // Create intance by framework
102
+ const data = await store.getData();
69
103
 
70
104
  if (process.env.NODE_ENV === "test") {
71
105
  assert.strictEqual(data, "mock");
@@ -74,24 +108,25 @@ if (process.env.NODE_ENV === "test") {
74
108
  }
75
109
  ```
76
110
 
77
- Using in Pro mode:
111
+ Using in Classic DI mode
78
112
  ------
113
+
79
114
  In professional mode Interfaces are used instead of implementations. But typescript does not generate Interfaces for working in runtime. But Interface is abstract base class. So instead of Interfaces, you need to write Abstract classes.
80
115
 
81
116
  ```typescript
82
- import { autowired, override, reflection } from "first-di";
117
+ import { resolve, override, reflection } from "first-di";
83
118
 
84
- abstract class AbstractRepository { // abstract instead of interface
119
+ abstract class AbstractRepository { // Abstract instead of interface
85
120
 
86
- abstract getData(): Promise<string>;
121
+ public abstract getData (): Promise<string>;
87
122
 
88
123
  }
89
124
 
90
125
  @reflection
91
126
  class ProdRepository implements AbstractRepository {
92
127
 
93
- public async getData(): Promise<string> {
94
- return await Promise.resolve("production");
128
+ public async getData (): Promise<string> {
129
+ return Promise.resolve("production");
95
130
  }
96
131
 
97
132
  }
@@ -99,15 +134,15 @@ class ProdRepository implements AbstractRepository {
99
134
  @reflection
100
135
  class MockRepository implements AbstractRepository {
101
136
 
102
- public async getData(): Promise<string> {
103
- return await Promise.resolve("mock");
137
+ public async getData (): Promise<string> {
138
+ return Promise.resolve("mock");
104
139
  }
105
140
 
106
141
  }
107
142
 
108
- abstract class AbstractService { // abstract instead of interface
143
+ abstract class AbstractService { // Abstract instead of interface
109
144
 
110
- abstract getData(): Promise<string>;
145
+ public abstract getData (): Promise<string>;
111
146
 
112
147
  }
113
148
 
@@ -116,25 +151,25 @@ class ProdService implements AbstractService {
116
151
 
117
152
  private readonly prodRepository: AbstractRepository;
118
153
 
119
- constructor(prodRepository: AbstractRepository) {
154
+ public constructor (prodRepository: AbstractRepository) {
120
155
  this.prodRepository = prodRepository;
121
156
  }
122
157
 
123
- public async getData(): Promise<string> {
124
- return await this.prodRepository.getData();
158
+ public async getData (): Promise<string> {
159
+ return this.prodRepository.getData();
125
160
  }
126
161
 
127
162
  }
128
163
 
129
- class ProdController {
130
-
131
- @autowired()
132
- private readonly prodService!: AbstractService;
164
+ @reflection
165
+ class ProdStore {
133
166
 
134
- // constructor use library, don't use him for inject
167
+ public constructor (
168
+ private readonly prodService: AbstractService
169
+ ) {}
135
170
 
136
- public async getData(): Promise<string> {
137
- return await this.prodService.getData();
171
+ public async getData (): Promise<string> {
172
+ return this.prodService.getData();
138
173
  }
139
174
 
140
175
  }
@@ -147,8 +182,8 @@ if (process.env.NODE_ENV === "test") {
147
182
  override(AbstractRepository, ProdRepository);
148
183
  }
149
184
 
150
- const controllerInstance = new ProdController();
151
- const data = await controllerInstance.getData();
185
+ const store = resolve(ProdStore);
186
+ const data = await store.getData();
152
187
 
153
188
  if (process.env.NODE_ENV === "test") {
154
189
  assert.strictEqual(data, "mock");
@@ -157,14 +192,17 @@ if (process.env.NODE_ENV === "test") {
157
192
  }
158
193
  ```
159
194
 
160
- Options:
195
+ Options
161
196
  ------
162
- First DI has several points for customizing dependency options.
197
+
198
+ First DI has several points for customizing dependency options:
199
+
163
200
  - **Global** - `DI.defaultOptions: AutowiredOptions`. Sets global default behavior.
164
- - **Autowired** - `@autowired(options?: AutowiredOptions)`. Sets behaviors for resolve dependencies.
165
201
  - **Override** - `override(fromClass, toClass, options?: AutowiredOptions)`. Sets behavior overrided dependency.
202
+ - **Resolve** - `resolve(class, options?: AutowiredOptions)`. Sets behaviors for resolve dependencies.
203
+
204
+ Options has next properties:
166
205
 
167
- AutowiredOptions has next properties:
168
206
  - **lifeTime: AutowiredLifetimes** - Sets lifeTime of dependecy.
169
207
 
170
208
  SINGLETON - Create one instance for all resolvers.
@@ -175,8 +213,9 @@ AutowiredOptions has next properties:
175
213
 
176
214
  PER_ACCESS - Create new instance on each access to resolved property.
177
215
 
178
- Scopes:
216
+ Scopes
179
217
  ------
218
+
180
219
  Support multiple scopes
181
220
 
182
221
  ```typescript
@@ -186,32 +225,18 @@ import { ProductionService } from "../services/ProductionService";
186
225
  const scopeA = new DI();
187
226
  const scopeB = new DI();
188
227
 
189
- export class Controller {
190
-
191
- @scopeA.autowired()
192
- private readonly serviceScopeA!: ProductionService;
193
-
194
- @scopeB.autowired()
195
- private readonly serviceScopeB!: ProductionService;
228
+ const serviceScopeA = scopeA.resolve(ProductionService);
229
+ const dataA = await serviceScopeA.getData();
196
230
 
197
- // constructor use library, don't use him for inject
198
-
199
- public async getDataScopeA(): Promise<string> {
200
- return await this.serviceScopeA.getData();
201
- }
202
-
203
- public async getDataScopeB(): Promise<string> {
204
- return await this.serviceScopeB.getData();
205
- }
206
-
207
- }
231
+ const serviceScopeB = scopeB.resolve(ProductionService);
232
+ const dataB = await serviceScopeB.getData();
208
233
  ```
209
234
 
210
- API:
235
+ API
211
236
  ------
212
- First DI also has an API for extended use. For example, use as A Service Locator.
213
237
 
214
- - autowired - Decorator. Assigned to property for resolve dependency.
238
+ First DI also has an API for extended use.
239
+
215
240
  - override - Function. Override dependency and resolve options.
216
241
  - resolve - Function. Resolves dependence with default options or specified.
217
242
  - singleton - Function. Resolve singleton.
@@ -221,16 +246,10 @@ First DI also has an API for extended use. For example, use as A Service Locator
221
246
  Resolve, singleton, instance - can be used to implement the Service Locator.
222
247
 
223
248
  ```typescript
224
- import { singleton, instance, resolve, autowired, AutowiredLifetimes } from "first-di";
249
+ import { singleton, instance, resolve, AutowiredLifetimes } from "first-di";
225
250
 
226
251
  class ApiDemo {
227
252
 
228
- @autowired({ lifeTime: AutowiredLifetimes.SINGLETON })
229
- private readonly service1!: ApiService1;
230
-
231
- @autowired({ lifeTime: AutowiredLifetimes.PER_INSTANCE })
232
- private readonly service2!: ApiService2;
233
-
234
253
  private readonly service3: ApiService3 = resolve(ApiService3, { lifeTime: AutowiredLifetimes.PER_INSTANCE });
235
254
 
236
255
  private readonly service4: ApiService4 = singleton(ApiService4);
@@ -240,10 +259,10 @@ class ApiDemo {
240
259
  }
241
260
  ```
242
261
 
243
-
244
- Extension DI:
262
+ Extension DI
245
263
  ------
246
- First DI using OOP and SOLID design principles. Each part of DI can be overrided or extended after inheritance from base class.
264
+
265
+ First DI using OOP and SOLID design principles. Each part of DI can be override or extende after inheritance from base class.
247
266
 
248
267
  ```typescript
249
268
  import { DI } from "first-di";
@@ -13,6 +13,11 @@ export declare class DI {
13
13
  protected singletonsList: Map<ClassConstructor<object>, object>;
14
14
  protected overrideList: Map<OverrideConstructor<object>, OverrideOptions>;
15
15
  constructor();
16
+ /**
17
+ * Decorator @autowired is deprecated, and will by removed in next versions
18
+ * @param options
19
+ * @returns
20
+ */
16
21
  protected makeAutowired(options?: AutowiredOptions): PropertyDecorator;
17
22
  protected makeResolve<T extends object>(inConstructor: ClassConstructor<T>, inOptions?: AutowiredOptions, caller?: object, propertyKey?: string | symbol): T;
18
23
  protected makeReset(): void;
@@ -18,7 +18,16 @@ export class DI {
18
18
  this.makeOverride(from, to, options);
19
19
  };
20
20
  }
21
+ /**
22
+ * Decorator @autowired is deprecated, and will by removed in next versions
23
+ * @param options
24
+ * @returns
25
+ */
21
26
  makeAutowired(options) {
27
+ // eslint-disable-next-line no-console
28
+ console.warn("first-di: @autowired is depreacted, " +
29
+ "new standart of ecmascript decorators prohibits changing the classes, " +
30
+ "use 'resolve' functions instaed of @autowired");
22
31
  return (target, propertyKey) => {
23
32
  const type = Reflect.getMetadata("design:type", target, propertyKey);
24
33
  const { resolve } = this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "first-di",
3
- "version": "1.0.19",
3
+ "version": "2.0.1",
4
4
  "description": "Easy dependency injection for typescript applications",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -9,15 +9,15 @@
9
9
  "reflect-metadata": ">=0.1.0"
10
10
  },
11
11
  "devDependencies": {
12
- "@labeg/code-style": "^4.0.4",
13
- "@types/chai": "^4.3.11",
12
+ "@labeg/code-style": "^4.0.7",
13
+ "@types/chai": "^4.3.14",
14
14
  "@types/mocha": "^10.0.6",
15
- "@types/node": "^20.10.4",
16
- "chai": "^4.3.10",
17
- "mocha": "^10.2.0",
15
+ "@types/node": "^20.12.7",
16
+ "chai": "^5.1.0",
17
+ "mocha": "^10.4.0",
18
18
  "ts-node": "^10.9.2",
19
- "reflect-metadata": "^0.2.1",
20
- "typescript": "^5.3.3"
19
+ "reflect-metadata": "^0.2.2",
20
+ "typescript": "^5.4.5"
21
21
  },
22
22
  "scripts": {
23
23
  "lint": "eslint --fix -c .eslintrc.cjs --ext .tsx,.ts,.jsx,.js ./src/ ./tests/",