smartdi 0.3.0 → 0.4.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.
Files changed (4) hide show
  1. package/README.md +252 -185
  2. package/index.d.ts +4 -0
  3. package/index.js +171 -42
  4. package/package.json +9 -4
package/README.md CHANGED
@@ -1,185 +1,252 @@
1
- # SmartDI
2
-
3
- A simple dependency injector based on decorators.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install smartdi
9
- ```
10
-
11
- ## Prerequisites
12
-
13
- Import the reflect-metadata package at the first line of your application.
14
-
15
- ```typescript
16
- import 'reflect-metadata'
17
-
18
- // Your code here...
19
- ```
20
-
21
- Enable "emitDecoratorMetadata" and "experimentalDecorators" in your tsconfig.json.
22
-
23
- ```json
24
- {
25
- "emitDecoratorMetadata": true,
26
- "experimentalDecorators": true,
27
- }
28
- ```
29
-
30
- ## Basic Usage
31
-
32
- ### Singlton
33
-
34
- ```typescript
35
- import 'reflect-metadata'
36
- import { SmartDI, Injectable, Inject } from 'smartdi';
37
-
38
- @Injectable()
39
- class ExampleService {
40
-
41
- sayHello() {
42
- console.log('Hello');
43
- }
44
-
45
- }
46
-
47
- @Injectable()
48
- class ExampleController {
49
-
50
- @Inject(ExampleService)
51
- private readonly myService!: ExampleService;
52
-
53
- sayHello() {
54
- this.myService.sayHello();
55
- }
56
-
57
- }
58
-
59
- const controller = SmartDI.get(ExampleController);
60
- controller.sayHello();
61
- ```
62
-
63
- ### Factory
64
-
65
- ```typescript
66
- import 'reflect-metadata'
67
- import { SmartDI, Injectable, Inject } from 'smartdi';
68
-
69
- interface Shape {
70
- area(): number;
71
- }
72
-
73
- // multiple means that the injectable is not singleton.
74
- @Injectable({name: 'circle', multiple: true})
75
- class Circle implements Shape {
76
-
77
- private radius: number;
78
-
79
- constructor(radius: number) {
80
- this.radius = radius;
81
- }
82
-
83
- area(): number {
84
- return Math.PI * this.radius * this.radius;
85
- }
86
-
87
- }
88
-
89
- @Injectable({name: 'rect', multiple: true})
90
- class Rect implements Shape {
91
-
92
- private width: number;
93
- private height: number;
94
-
95
- constructor(width: number, height: number) {
96
- this.width = width;
97
- this.height = height;
98
- }
99
-
100
- area(): number {
101
- return this.width * this.height;
102
- }
103
-
104
- }
105
-
106
- const type1 = 'circle';
107
- const args1 = [5]; // radius = 5;
108
- const shape1 = SmartDI.get<Shape>(type1, ...args1);
109
- const area1 = shape1.area();
110
- const type2 = 'rect';
111
- const args2 = [3, 4]; // width = 3, height = 4;
112
- const shape2 = SmartDI.get<Shape>(type2, ...args2);
113
- const area2 = shape2.area();
114
-
115
- ```
116
-
117
- ### Circular Dependency
118
-
119
- ```typescript
120
- import 'reflect-metadata'
121
- import { SmartDI, Injectable, Inject } from 'smartdi';
122
-
123
- @Injectable()
124
- class ExampleServiceA {
125
-
126
- @Inject(ExampleServiceB)
127
- private readonly exampleServiceB!: ExampleServiceB;
128
-
129
- }
130
-
131
- @Injectable()
132
- class ExampleServiceB {
133
-
134
- @Inject(ExampleServiceA)
135
- private readonly exampleServiceA!: ExampleServiceA;
136
-
137
- }
138
-
139
- const serviceA = SmartDI.get(ExampleServiceA);
140
- const serviceB = SmartDI.get(ExampleServiceB);
141
-
142
- ```
143
-
144
- ## APIs
145
-
146
- ### @Injectable(options?)
147
-
148
- Decorate a class as injectable. An "injectable" refers to a class that can be instantiated and injected into other components.
149
-
150
- - options: Injectable options.
151
-
152
- | name | description |
153
- | --- | --- |
154
- | name | The name of the injectable. If not specified, the class name will be used. |
155
- | multiple | Whether the injectable is a factory. If not specified, the injectable is a singleton. |
156
- | args | Default arguments of the injectable's constructor. default is []. |
157
-
158
- ### @Inject(cule, ...args)
159
-
160
- Decorate a property as injectable, which will be injected according to the specified clue.
161
-
162
- - clue: Name of the injectable or type of the injectable.
163
- - args: Arguments to pass to constructor.
164
-
165
- ### @InjectArray(clue, ...args)
166
-
167
- Decorate a property as injectable, which will be injected according to the specified clue.
168
- The property's type must be Array and the clue must be a type. All injectables that are subtypes of the specified type will be injected.
169
-
170
- - clue: Type of the injectable.
171
- - args: Arguments to pass to constructor.
172
-
173
- ### SmartDI.get(clue, ...args)
174
-
175
- Get the injectable by name or type.
176
-
177
- - clue: Name of the injectable or type of the injectable.
178
- - args: Arguments to pass to constructor.
179
-
180
- ## Limitations
181
-
182
- Injection is processed after construction, so do not access injectable values in the constructor.
183
-
184
- ## License
185
- MIT
1
+ # SmartDI
2
+
3
+ A simple dependency injector based on decorators.
4
+ 基于装饰器的简单依赖注入器。
5
+
6
+ ## Installation | 安装
7
+
8
+ ```bash
9
+ npm install smartdi
10
+ ```
11
+
12
+ ## Prerequisites | 先决条件
13
+
14
+ Import the `reflect-metadata` package on the first line of your application.
15
+ 在应用程序的第一行导入 `reflect-metadata` 包。
16
+
17
+ ```typescript
18
+ import 'reflect-metadata'
19
+
20
+ // Your code here...
21
+ ```
22
+
23
+ Enable `emitDecoratorMetadata` and `experimentalDecorators` in your `tsconfig.json`.
24
+ 在 `tsconfig.json` 中启用 `emitDecoratorMetadata` 和 `experimentalDecorators`。
25
+
26
+ ```json
27
+ {
28
+ "emitDecoratorMetadata": true,
29
+ "experimentalDecorators": true,
30
+ }
31
+ ```
32
+
33
+ ## Basic Usage | 基本用法
34
+
35
+ ### Singleton | 单例
36
+
37
+ ```typescript
38
+ import 'reflect-metadata'
39
+ import { SmartDI, Injectable, Inject } from 'smartdi';
40
+
41
+ @Injectable()
42
+ class ExampleService {
43
+
44
+ sayHello() {
45
+ console.log('Hello');
46
+ }
47
+
48
+ }
49
+
50
+ @Injectable()
51
+ class ExampleController {
52
+
53
+ @Inject(ExampleService)
54
+ private readonly myService!: ExampleService;
55
+
56
+ sayHello() {
57
+ this.myService.sayHello();
58
+ }
59
+
60
+ }
61
+
62
+ const controller = SmartDI.get(ExampleController);
63
+ controller.sayHello();
64
+ ```
65
+
66
+ ### Factory | 工厂模式
67
+
68
+ ```typescript
69
+ import 'reflect-metadata'
70
+ import { SmartDI, Injectable, Inject } from 'smartdi';
71
+
72
+ interface Shape {
73
+ area(): number;
74
+ }
75
+
76
+ // multiple means that the injectable is not singleton.
77
+ @Injectable({name: 'circle', multiple: true})
78
+ class Circle implements Shape {
79
+
80
+ private radius: number;
81
+
82
+ constructor(radius: number) {
83
+ this.radius = radius;
84
+ }
85
+
86
+ area(): number {
87
+ return Math.PI * this.radius * this.radius;
88
+ }
89
+
90
+ }
91
+
92
+ @Injectable({name: 'rect', multiple: true})
93
+ class Rect implements Shape {
94
+
95
+ private width: number;
96
+ private height: number;
97
+
98
+ constructor(width: number, height: number) {
99
+ this.width = width;
100
+ this.height = height;
101
+ }
102
+
103
+ area(): number {
104
+ return this.width * this.height;
105
+ }
106
+
107
+ }
108
+
109
+ const type1 = 'circle';
110
+ const args1 = [5]; // radius = 5;
111
+ const shape1 = SmartDI.get<Shape>(type1, ...args1);
112
+ const area1 = shape1.area();
113
+ const type2 = 'rect';
114
+ const args2 = [3, 4]; // width = 3, height = 4;
115
+ const shape2 = SmartDI.get<Shape>(type2, ...args2);
116
+ const area2 = shape2.area();
117
+
118
+ ```
119
+
120
+ ### Circular Dependency | 循环依赖
121
+
122
+ ```typescript
123
+ import 'reflect-metadata'
124
+ import { SmartDI, Injectable, Inject } from 'smartdi';
125
+
126
+ @Injectable()
127
+ class ExampleServiceA {
128
+
129
+ @Inject(ExampleServiceB)
130
+ private readonly exampleServiceB!: ExampleServiceB;
131
+
132
+ }
133
+
134
+ @Injectable()
135
+ class ExampleServiceB {
136
+
137
+ @Inject(ExampleServiceA)
138
+ private readonly exampleServiceA!: ExampleServiceA;
139
+
140
+ }
141
+
142
+ const serviceA = SmartDI.get(ExampleServiceA);
143
+ const serviceB = SmartDI.get(ExampleServiceB);
144
+
145
+ ```
146
+
147
+ ## APIs | 接口
148
+
149
+ ### @Injectable(options?)
150
+
151
+ Decorate a class as injectable. An "injectable" refers to a class that can be instantiated and injected into other components.
152
+ 将类装饰为 `injectable`。`injectable` 类是指可以被实例化并注入到其他组件中的类。
153
+
154
+ - options: Injectable options.
155
+ - options: 可注入选项。
156
+
157
+ | name \| 字段名 | description \| 描述 |
158
+ | --- | --- |
159
+ | name | The name of the `injectable`. If not specified, the class name will be used.<br>`injectable` 的名称。若未指定,则使用类名。 |
160
+ | multiple | Whether the `injectable` is a factory. If not specified, the `injectable` is a singleton.<br>`injectable` 的是否为工厂模式。若未指定,则为单例。 |
161
+ | args | Default arguments of the `injectable`'s constructor. Defaults to [].<br>`injectable` 的构造函数的默认参数。默认为 []。 |
162
+
163
+ ### @Inject(clue, ...args)
164
+
165
+ Decorate a property as a candidate for injection, which will be injected according to the specified clue.
166
+ 将属性装饰为待注入的,将根据指定的线索进行注入。
167
+
168
+ - clue: Name, type, or abstract class of `injectable`.
169
+ - clue: `injectable` 的名称、类型或抽象类。
170
+ - args: Arguments to pass to constructor.
171
+ - args: 传递给构造函数的参数。
172
+
173
+ ### @InjectArray(clue, ...args)
174
+
175
+ Decorate a property as a candidate for injection, which will be injected according to the specified clue.
176
+ 将属性装饰为待注入的,将根据指定的线索进行注入。
177
+ The property's type must be `Array` and the clue must be a type. All injectables that are subtypes of the specified type will be injected.
178
+ 属性的类型必须为 `Array`,且线索必须为 `injectable` 的类型。指定 `injectable` 类型的所有子类的 `injectable` 都将被注入。
179
+
180
+ - clue: Type or abstract class of the `injectable`.
181
+ - clue: `injectable` 的类型或抽象类。
182
+ - args: Arguments to pass to constructor.
183
+ - args:传递给构造函数的参数。
184
+
185
+ ### SmartDI.get(clue, ...args)
186
+
187
+ Get an instance of the `injectable` by name or type.
188
+ 根据名称或类型获取 `injectable` 的实例。
189
+
190
+ - clue: Name of the `injectable` or type of the injectable.
191
+ - clue: `injectable` 的名称或类型。
192
+ - args: Arguments to pass to constructor.
193
+ - args: 传递给构造函数的参数。
194
+
195
+ ### SmartDI.register(clazz, options?)
196
+
197
+ Programmatically register a class as an `injectable`, serving as an alternative to the `@Injectable` decorator.
198
+ 以编程方式注册类为 `injectable`,作为 `@Injectable` 装饰器的替代方案。
199
+
200
+ - clazz: Class to register.
201
+ - clazz: 要注册的类。
202
+ - options: Same as `@Injectable` options.
203
+ - options: 与 `@Injectable` 的选项相同。
204
+
205
+ ### SmartDI.getArray(clue, ...args)
206
+
207
+ Get all registered subtypes of the specified type as an array.
208
+ 以数组形式获取指定类型的所有已注册子类型的实例。
209
+
210
+ - clue: Type or abstract class of the `injectable`.
211
+ - clue: `injectable` 的类型或抽象类。
212
+ - args: Arguments to pass to constructor.
213
+ - args: 传递给构造函数的参数。
214
+
215
+ ### SmartDI.initialize()
216
+
217
+ Create all singleton instances and invoke all methods decorated with `@PostConstruct`. Supports both synchronous and asynchronous lifecycle hooks.
218
+ 创建所有单例实例,并调用所有使用 `@PostConstruct` 装饰的方法。支持同步和异步的生命周期钩子。
219
+
220
+ ### SmartDI.uninitialize()
221
+
222
+ Clear all singleton instances in reverse registration order, invoking the `@PreDestroy` decorated methods for each.
223
+ 按注册逆序清空所有单例实例,并调用使用 `@PreDestroy` 装饰的方法。
224
+
225
+ ### @PostConstruct()
226
+
227
+ Decorate a method as a lifecycle hook, which will be invoked when `SmartDI.initialize()` is called.
228
+ 将方法装饰为生命周期钩子,在调用 `SmartDI.initialize()` 时执行。
229
+
230
+ ### @PreDestroy()
231
+
232
+ Decorate a method as a lifecycle hook, which will be invoked when `SmartDI.uninitialize()` is called.
233
+ 将方法装饰为生命周期钩子,在调用 `SmartDI.uninitialize()` 时执行。
234
+
235
+ ## Inheritance | 继承
236
+
237
+ Injected properties and lifecycle hooks declared on base classes apply to subclass instances. Decorators write only the metadata of the class they decorate; the prototype chain is merged when an instance is created.
238
+
239
+ 基类声明的注入属性与生命周期钩子会对子类实例生效。装饰器只写入所在类自己的元数据,实例创建时再沿原型链合并。
240
+
241
+ - Injected properties are merged base-first, so a subclass property with the same name overrides the base one.
242
+ 注入属性按基类在前合并,子类同名字段覆盖基类字段。
243
+ - `@PostConstruct` / `@PreDestroy` methods run once per instance, base-class hooks first.
244
+ 每个实例的 `@PostConstruct` / `@PreDestroy` 各执行一次,基类钩子在前。
245
+
246
+ ## Limitations
247
+
248
+ Injection is processed after construction, so do not access injectable values in the constructor.
249
+ 注入是在构造函数执行之后再进行处理的,因此不要在构造函数中访问可注入的值。
250
+
251
+ ## License
252
+ MIT
package/index.d.ts CHANGED
@@ -12,8 +12,12 @@ export declare abstract class SmartDI {
12
12
  static get<T>(clue: Class<T> | AbstractClass<T>, ...args: any[]): T;
13
13
  static get<T>(name: string, ...args: any[]): T;
14
14
  static getArray<T>(clue: Class<T> | AbstractClass<T>, ...args: any[]): T[];
15
+ static initialize(): Promise<void>;
16
+ static uninitialize(): Promise<void>;
15
17
  }
16
18
  export declare function Injectable(options?: InjectableOptions): ClassDecorator;
17
19
  export declare function Inject(constructor: Class<any> | AbstractClass<any>, ...args: any[]): PropertyDecorator;
18
20
  export declare function Inject(name: string, ...args: any[]): PropertyDecorator;
19
21
  export declare function InjectArray(constructor: Class<any> | AbstractClass<any>, ...args: any[]): PropertyDecorator;
22
+ export declare function PostConstruct(): MethodDecorator;
23
+ export declare function PreDestroy(): MethodDecorator;
package/index.js CHANGED
@@ -1,8 +1,82 @@
1
1
  "use strict";
2
- var _a;
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var _a, _b;
3
12
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.InjectArray = exports.Inject = exports.Injectable = exports.SmartDI = void 0;
13
+ exports.SmartDI = void 0;
14
+ exports.Injectable = Injectable;
15
+ exports.Inject = Inject;
16
+ exports.InjectArray = InjectArray;
17
+ exports.PostConstruct = PostConstruct;
18
+ exports.PreDestroy = PreDestroy;
5
19
  const $registry = Symbol('registry');
20
+ const $singletons = Symbol('singletons');
21
+ const METADATA_INJECTS = 'smartdi:injects';
22
+ const METADATA_POST_CONSTRUCT = 'smartdi:post-construct';
23
+ const METADATA_PRE_DESTROY = 'smartdi:pre-destroy';
24
+ function prototypeChain(instance) {
25
+ const chain = [];
26
+ let proto = Object.getPrototypeOf(instance);
27
+ while (proto && proto !== Object.prototype) {
28
+ chain.push(proto);
29
+ proto = Object.getPrototypeOf(proto);
30
+ }
31
+ return chain.reverse();
32
+ }
33
+ function isSubclass(sub, sup) {
34
+ let proto = Object.getPrototypeOf(sub);
35
+ while (proto) {
36
+ if (proto === sup)
37
+ return true;
38
+ proto = Object.getPrototypeOf(proto);
39
+ }
40
+ return false;
41
+ }
42
+ const injectsCache = new WeakMap();
43
+ function collectInjects(instance) {
44
+ const prototype = Object.getPrototypeOf(instance);
45
+ let merged = injectsCache.get(prototype);
46
+ if (merged)
47
+ return merged;
48
+ merged = {};
49
+ for (const proto of prototypeChain(instance))
50
+ Object.assign(merged, Reflect.getOwnMetadata(METADATA_INJECTS, proto));
51
+ injectsCache.set(prototype, merged);
52
+ return merged;
53
+ }
54
+ function collectLifecycle(instance, key) {
55
+ var _c;
56
+ const methods = [];
57
+ const seen = new Set();
58
+ for (const proto of prototypeChain(instance)) {
59
+ for (const method of (_c = Reflect.getOwnMetadata(key, proto)) !== null && _c !== void 0 ? _c : []) {
60
+ if (seen.has(method))
61
+ continue;
62
+ seen.add(method);
63
+ methods.push(method);
64
+ }
65
+ }
66
+ return methods;
67
+ }
68
+ function defineInject(target, prop, item) {
69
+ var _c;
70
+ const injects = (_c = Reflect.getOwnMetadata(METADATA_INJECTS, target)) !== null && _c !== void 0 ? _c : {};
71
+ injects[prop] = item;
72
+ Reflect.defineMetadata(METADATA_INJECTS, injects, target);
73
+ }
74
+ function appendLifecycle(target, key, method) {
75
+ var _c;
76
+ const methods = (_c = Reflect.getOwnMetadata(key, target)) !== null && _c !== void 0 ? _c : [];
77
+ methods.push(method);
78
+ Reflect.defineMetadata(key, methods, target);
79
+ }
6
80
  class SmartDI {
7
81
  static register(clazz, options) {
8
82
  const name = (options === null || options === void 0 ? void 0 : options.name) || clazz.name;
@@ -10,13 +84,16 @@ class SmartDI {
10
84
  const singleton = !(options === null || options === void 0 ? void 0 : options.multiple);
11
85
  if (SmartDI[$registry][name])
12
86
  throw new Error(`Duplicated injectable name '${name}'!`);
13
- SmartDI[$registry][name] = {
87
+ const item = {
14
88
  name,
15
89
  class: clazz,
16
90
  args: args,
17
91
  singleton,
18
92
  instance: undefined
19
93
  };
94
+ if (singleton)
95
+ SmartDI[$singletons].push(item);
96
+ SmartDI[$registry][name] = item;
20
97
  }
21
98
  static get(clue, ...args) {
22
99
  return SmartDI.internalGet(clue, new Set(), ...args);
@@ -24,6 +101,50 @@ class SmartDI {
24
101
  static getArray(clue, ...args) {
25
102
  return SmartDI.internalGetArray(clue, new Set(), ...args);
26
103
  }
104
+ static initialize() {
105
+ return __awaiter(this, void 0, void 0, function* () {
106
+ const instances = [];
107
+ for (const item of SmartDI[$singletons])
108
+ instances.push(SmartDI.obtainInstance(item, new Set()));
109
+ const promises = [];
110
+ for (const instance of instances) {
111
+ const methods = collectLifecycle(instance, METADATA_POST_CONSTRUCT);
112
+ if (!methods.length)
113
+ continue;
114
+ for (const method of methods) {
115
+ const result = instance[method]();
116
+ if (result instanceof Promise)
117
+ promises.push(result);
118
+ }
119
+ }
120
+ if (!promises.length)
121
+ return;
122
+ yield Promise.all(promises);
123
+ });
124
+ }
125
+ static uninitialize() {
126
+ return __awaiter(this, void 0, void 0, function* () {
127
+ const items = [...SmartDI[$singletons]].reverse();
128
+ const promises = [];
129
+ for (const item of items) {
130
+ const instance = item.instance;
131
+ if (!instance)
132
+ continue;
133
+ item.instance = undefined;
134
+ const methods = collectLifecycle(instance, METADATA_PRE_DESTROY);
135
+ if (!methods.length)
136
+ continue;
137
+ for (const method of methods) {
138
+ const result = instance[method]();
139
+ if (result instanceof Promise)
140
+ promises.push(result);
141
+ }
142
+ }
143
+ if (!promises.length)
144
+ return;
145
+ yield Promise.all(promises);
146
+ });
147
+ }
27
148
  static internalGet(clue, creatings, ...args) {
28
149
  let constructor, name;
29
150
  if (typeof clue === 'function') {
@@ -36,14 +157,14 @@ class SmartDI {
36
157
  }
37
158
  let item = SmartDI[$registry][name];
38
159
  if (!item && constructor)
39
- item = SmartDI.resovleOneByConstructor(constructor);
160
+ item = SmartDI.resolveOneByConstructor(constructor);
40
161
  if (!item)
41
162
  throw new Error(`Can not resolve injectable '${name}'!`);
42
163
  return SmartDI.obtainInstance(item, creatings, ...args);
43
164
  }
44
165
  static internalGetArray(constructor, creatings, ...args) {
45
- const items = SmartDI.resovleByConstructor(constructor);
46
- if (items.length == 0)
166
+ const items = SmartDI.resolveByConstructor(constructor);
167
+ if (items.length === 0)
47
168
  throw new Error(`Can not resolve injectable '${constructor.name}'!`);
48
169
  const instances = [];
49
170
  for (const item of items)
@@ -53,70 +174,78 @@ class SmartDI {
53
174
  static obtainInstance(item, creatings, ...args) {
54
175
  if (item.instance)
55
176
  return item.instance;
56
- if (args.length == 0)
177
+ if (args.length === 0)
57
178
  args = item.args;
58
179
  const instance = new item.class(...args);
59
180
  if (item.singleton)
60
181
  item.instance = instance;
61
- const injects = Reflect.getMetadata('smartdi:injects', instance);
62
- if (!injects)
182
+ const injects = collectInjects(instance);
183
+ const injectKeys = Reflect.ownKeys(injects);
184
+ if (!injectKeys.length)
63
185
  return instance;
64
186
  if (!item.singleton && creatings.has(item.name))
65
187
  throw new Error('Circular dependency detected!');
66
188
  creatings.add(item.name);
67
- for (const prop of Object.keys(injects))
68
- instance[prop] = injects[prop].array
69
- ? SmartDI.internalGetArray(injects[prop].clue, creatings, ...injects[prop].args)
70
- : SmartDI.internalGet(injects[prop].clue, creatings, ...injects[prop].args);
189
+ try {
190
+ for (const prop of injectKeys)
191
+ instance[prop] = injects[prop].array
192
+ ? SmartDI.internalGetArray(injects[prop].clue, creatings, ...injects[prop].args)
193
+ : SmartDI.internalGet(injects[prop].clue, creatings, ...injects[prop].args);
194
+ }
195
+ catch (e) {
196
+ if (item.singleton)
197
+ item.instance = undefined;
198
+ throw e;
199
+ }
71
200
  return instance;
72
201
  }
73
- static resovleByConstructor(constructor) {
74
- let matched = [];
75
- function isSubclass(sub, sup) {
76
- let proto = Object.getPrototypeOf(sub);
77
- while (proto) {
78
- if (proto === sup)
79
- return true;
80
- proto = Object.getPrototypeOf(proto);
81
- }
82
- return false;
83
- }
202
+ static resolveByConstructor(constructor) {
203
+ const matched = [];
84
204
  for (const item of Object.values(SmartDI[$registry])) {
85
- if (!isSubclass(item.class, constructor))
86
- continue;
87
- matched.push(item);
205
+ if (isSubclass(item.class, constructor))
206
+ matched.push(item);
88
207
  }
89
208
  return matched;
90
209
  }
91
- static resovleOneByConstructor(constructor) {
92
- const items = SmartDI.resovleByConstructor(constructor);
93
- if (items.length > 1)
94
- throw new Error(`Multiple injectable classes found for '${constructor.name}': ${items.map(item => item.name).join(', ')}`);
95
- return items[0];
210
+ static resolveOneByConstructor(constructor) {
211
+ const matched = [];
212
+ for (const item of Object.values(SmartDI[$registry])) {
213
+ if (item.class === constructor)
214
+ return item;
215
+ if (isSubclass(item.class, constructor))
216
+ matched.push(item);
217
+ }
218
+ if (matched.length > 1)
219
+ throw new Error(`Multiple injectable classes found for '${constructor.name}': ${matched.map(item => item.name).join(', ')}`);
220
+ return matched[0];
96
221
  }
97
222
  }
98
223
  exports.SmartDI = SmartDI;
99
- _a = $registry;
224
+ _a = $registry, _b = $singletons;
100
225
  SmartDI[_a] = {};
226
+ SmartDI[_b] = [];
101
227
  function Injectable(options) {
102
228
  return (target) => {
103
229
  SmartDI.register(target, options);
104
230
  };
105
231
  }
106
- exports.Injectable = Injectable;
107
232
  function Inject(clue, ...args) {
108
233
  return function (target, prop) {
109
- let injects = Reflect.getMetadata('smartdi:injects', target) || {};
110
- injects[prop] = { array: false, clue, args };
111
- Reflect.defineMetadata('smartdi:injects', injects, target);
234
+ defineInject(target, prop, { array: false, clue, args });
112
235
  };
113
236
  }
114
- exports.Inject = Inject;
115
237
  function InjectArray(clue, ...args) {
116
238
  return function (target, prop) {
117
- let injects = Reflect.getMetadata('smartdi:injects', target) || {};
118
- injects[prop] = { array: true, clue, args };
119
- Reflect.defineMetadata('smartdi:injects', injects, target);
239
+ defineInject(target, prop, { array: true, clue, args });
240
+ };
241
+ }
242
+ function PostConstruct() {
243
+ return function (target, key, _descriptor) {
244
+ appendLifecycle(target, METADATA_POST_CONSTRUCT, key);
245
+ };
246
+ }
247
+ function PreDestroy() {
248
+ return function (target, key, _descriptor) {
249
+ appendLifecycle(target, METADATA_PRE_DESTROY, key);
120
250
  };
121
251
  }
122
- exports.InjectArray = InjectArray;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "smartdi",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "A simple dependency injector based on decorators.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "build": "tsc -p tsconfig.build.json",
8
+ "test": "jest"
8
9
  },
9
10
  "keywords": [
10
11
  "DI",
@@ -27,7 +28,11 @@
27
28
  },
28
29
  "homepage": "https://github.com/iwares/smartdi#readme",
29
30
  "devDependencies": {
31
+ "@types/jest": "^30.0.0",
30
32
  "@types/node": "^22.13.10",
31
- "reflect-metadata": "^0.2.2"
33
+ "jest": "^30.5.2",
34
+ "reflect-metadata": "^0.2.2",
35
+ "ts-jest": "^29.4.15",
36
+ "typescript": "^6.0.3"
32
37
  }
33
- }
38
+ }