smartdi 0.0.1 → 0.1.0
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 +185 -3
- package/index.d.ts +29 -84
- package/index.js +90 -163
- package/package.json +33 -33
- package/index.js.map +0 -1
- package/index.ts +0 -286
- package/tsconfig.json +0 -14
package/README.md
CHANGED
|
@@ -1,3 +1,185 @@
|
|
|
1
|
-
# SmartDI
|
|
2
|
-
|
|
3
|
-
A simple dependency injector based on decorators.
|
|
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
|
package/index.d.ts
CHANGED
|
@@ -1,88 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Class.
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
export declare type Class<T = any> = {
|
|
7
|
-
new (arg?: any): T;
|
|
1
|
+
export type Class<T> = {
|
|
2
|
+
new (...args: any[]): T;
|
|
8
3
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[key: string]: any;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Indicates that a decorated class is a "component". Such classes are considered as candidates for DI when scanning.
|
|
18
|
-
*
|
|
19
|
-
* @param name - name of this component(optional)
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
export declare function Component(options?: ComponentOptions, multiple?: boolean): ClassDecorator;
|
|
23
|
-
export declare function Component(name: string, multiple?: boolean): ClassDecorator;
|
|
24
|
-
export declare function Component(name: string, options: ComponentOptions, multiple?: boolean): ClassDecorator;
|
|
25
|
-
/**
|
|
26
|
-
* Marks a field to be autowired by SmartDI's dependency injection facilities.
|
|
27
|
-
*
|
|
28
|
-
* @param constructor - class or base class of the autowired component
|
|
29
|
-
* @param options - options used to creating new instances.
|
|
30
|
-
*
|
|
31
|
-
*/
|
|
32
|
-
export declare function Autowired(constructor: Class, options?: ComponentOptions): PropertyDecorator;
|
|
33
|
-
/**
|
|
34
|
-
* Marks a field to be autowired by SmartDI's dependency injection facilities.
|
|
35
|
-
*
|
|
36
|
-
* @param name - name of the autowired component
|
|
37
|
-
* @param options - options used to creating new instances.
|
|
38
|
-
*
|
|
39
|
-
*/
|
|
40
|
-
export declare function Autowired(name: string, options?: ComponentOptions): PropertyDecorator;
|
|
41
|
-
/**
|
|
42
|
-
* Options to indicate how SmartDI wire up all components.
|
|
43
|
-
*
|
|
44
|
-
*/
|
|
45
|
-
export interface AutowireOptions {
|
|
46
|
-
/** built-in components */
|
|
47
|
-
components?: {
|
|
48
|
-
[key: string]: any;
|
|
49
|
-
};
|
|
50
|
-
/** dirs to scan compoents */
|
|
51
|
-
scans?: Array<string>;
|
|
4
|
+
declare const $registry: unique symbol;
|
|
5
|
+
export interface InjectableOptions {
|
|
6
|
+
name?: string;
|
|
7
|
+
args?: any[];
|
|
8
|
+
multiple?: boolean;
|
|
52
9
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* @param options - autowire options
|
|
67
|
-
*
|
|
68
|
-
*/
|
|
69
|
-
static autowire(options: AutowireOptions): void;
|
|
70
|
-
/**
|
|
71
|
-
* Get a component which is a instance of the specified class.
|
|
72
|
-
*
|
|
73
|
-
* @param constructor - class or base class of the compoent
|
|
74
|
-
* @param options - options used to creating new instance.
|
|
75
|
-
*
|
|
76
|
-
*/
|
|
77
|
-
static get<T>(constructor: Class<T>, options?: ComponentOptions): T;
|
|
78
|
-
/**
|
|
79
|
-
* Get a component by name.
|
|
80
|
-
*
|
|
81
|
-
* @param name - name of the compoent
|
|
82
|
-
* @param options - options used to creating new instance.
|
|
83
|
-
*
|
|
84
|
-
*/
|
|
85
|
-
static get(name: string, options?: ComponentOptions): any;
|
|
10
|
+
interface RegistryItem {
|
|
11
|
+
name: string;
|
|
12
|
+
class: Class<any>;
|
|
13
|
+
args: any[];
|
|
14
|
+
singleton: boolean;
|
|
15
|
+
instance: any;
|
|
16
|
+
}
|
|
17
|
+
export declare abstract class SmartDI {
|
|
18
|
+
static readonly [$registry]: {
|
|
19
|
+
[key: string]: RegistryItem;
|
|
20
|
+
};
|
|
21
|
+
static get<T>(clue: Class<T>, ...args: any[]): T;
|
|
22
|
+
static get<T>(name: string, ...args: any[]): T;
|
|
86
23
|
private static internalGet;
|
|
87
|
-
private static
|
|
24
|
+
private static internalGetArray;
|
|
25
|
+
private static obtainInstance;
|
|
26
|
+
private static resovleByConstructor;
|
|
27
|
+
private static resovleOneByConstructor;
|
|
88
28
|
}
|
|
29
|
+
export declare function Injectable(options?: InjectableOptions): ClassDecorator;
|
|
30
|
+
export declare function Inject(constructor: Class<any>, ...args: any[]): PropertyDecorator;
|
|
31
|
+
export declare function Inject(name: string, ...args: any[]): PropertyDecorator;
|
|
32
|
+
export declare function InjectArray(constructor: Class<any>, ...args: any[]): PropertyDecorator;
|
|
33
|
+
export {};
|
package/index.js
CHANGED
|
@@ -1,189 +1,116 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
2
|
+
var _a;
|
|
5
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
const
|
|
8
|
-
require("reflect-metadata");
|
|
9
|
-
function Component(arg1, arg2, arg3) {
|
|
10
|
-
return function (target) {
|
|
11
|
-
let name, singleton, options;
|
|
12
|
-
if (typeof arg1 === 'undefined' || typeof arg1 === 'object') {
|
|
13
|
-
name = target.name;
|
|
14
|
-
options = arg1;
|
|
15
|
-
singleton = !arg2;
|
|
16
|
-
}
|
|
17
|
-
else if (typeof arg2 === 'boolean') {
|
|
18
|
-
name = arg1;
|
|
19
|
-
options = undefined;
|
|
20
|
-
singleton = !arg2;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
name = arg1;
|
|
24
|
-
options = arg2;
|
|
25
|
-
singleton = !arg3;
|
|
26
|
-
}
|
|
27
|
-
Reflect.defineMetadata('smartdi:component', name, target);
|
|
28
|
-
Reflect.defineMetadata('smartdi:singleton', singleton, target);
|
|
29
|
-
Reflect.defineMetadata('smartdi:options', options, target);
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
exports.Component = Component;
|
|
33
|
-
function Autowired(arg1, options) {
|
|
34
|
-
return function (target, prop) {
|
|
35
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', target) || {};
|
|
36
|
-
autowireds[prop] = { clue: arg1, options: options };
|
|
37
|
-
Reflect.defineMetadata('smartdi:autowireds', autowireds, target);
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
exports.Autowired = Autowired;
|
|
41
|
-
/**
|
|
42
|
-
* Main entry of SmartDI framework.
|
|
43
|
-
*
|
|
44
|
-
*/
|
|
4
|
+
exports.InjectArray = exports.Inject = exports.Injectable = exports.SmartDI = void 0;
|
|
5
|
+
const $registry = Symbol('registry');
|
|
45
6
|
class SmartDI {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Wire up all components according to specified options.
|
|
51
|
-
*
|
|
52
|
-
* @param options - autowire options
|
|
53
|
-
*
|
|
54
|
-
*/
|
|
55
|
-
static autowire(options) {
|
|
56
|
-
if (SmartDI.autowired)
|
|
57
|
-
throw new Error('SmartDI.autowire() can only be called once.');
|
|
58
|
-
SmartDI.autowired = true;
|
|
59
|
-
for (let name of Object.keys(options.components || {})) {
|
|
60
|
-
let component = options.components[name];
|
|
61
|
-
SmartDI.registry[name] = {
|
|
62
|
-
source: 'built-in',
|
|
63
|
-
name: name,
|
|
64
|
-
class: component.constructor,
|
|
65
|
-
options: undefined,
|
|
66
|
-
singleton: component,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// Iterate all dirs to find and instantiate all components.
|
|
70
|
-
for (let scan of options.scans || []) {
|
|
71
|
-
// Load all modules.
|
|
72
|
-
let modules = require_all_1.default({ dirname: scan, filter: /.+\.js$/ });
|
|
73
|
-
// Iterate all modules.
|
|
74
|
-
for (let module of Object.values(modules)) {
|
|
75
|
-
// Iterate all exported symbols
|
|
76
|
-
for (let symbol of Object.values(module)) {
|
|
77
|
-
// Not a function, ignore.
|
|
78
|
-
if (typeof symbol !== 'function')
|
|
79
|
-
continue;
|
|
80
|
-
// Fetch metadata, ignore if not found.
|
|
81
|
-
let name = Reflect.getMetadata('smartdi:component', symbol);
|
|
82
|
-
if (!name)
|
|
83
|
-
continue;
|
|
84
|
-
// Check conflicts
|
|
85
|
-
let exist = SmartDI.registry[name];
|
|
86
|
-
if (exist)
|
|
87
|
-
throw new Error(`Duplicate component name: ${name}`);
|
|
88
|
-
let singleton = Reflect.getMetadata('smartdi:singleton', symbol);
|
|
89
|
-
let options = Reflect.getMetadata('smartdi:options', symbol);
|
|
90
|
-
// Add to registry.
|
|
91
|
-
SmartDI.registry[name] = {
|
|
92
|
-
source: 'scan',
|
|
93
|
-
name: name,
|
|
94
|
-
class: symbol,
|
|
95
|
-
options: options,
|
|
96
|
-
singleton: singleton ? new symbol(options) : undefined,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Iterate registry and inject dependencies.
|
|
102
|
-
for (let item of Object.values(SmartDI.registry)) {
|
|
103
|
-
// Fetch metadata;
|
|
104
|
-
if (!item.singleton)
|
|
105
|
-
continue;
|
|
106
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', item.singleton);
|
|
107
|
-
if (!autowireds)
|
|
108
|
-
continue;
|
|
109
|
-
// Inject components.
|
|
110
|
-
for (let prop of Object.keys(autowireds)) {
|
|
111
|
-
item.singleton[prop] = SmartDI.get(autowireds[prop].clue, autowireds[prop].options);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
7
|
+
static get(clue, ...args) {
|
|
8
|
+
return SmartDI.internalGet(clue, new Set(), ...args);
|
|
114
9
|
}
|
|
115
|
-
static
|
|
116
|
-
return SmartDI.internalGet(arg, options);
|
|
117
|
-
}
|
|
118
|
-
static internalGet(arg, options, creatings) {
|
|
10
|
+
static internalGet(clue, creatings, ...args) {
|
|
119
11
|
let constructor, name;
|
|
120
|
-
if (typeof
|
|
121
|
-
constructor =
|
|
12
|
+
if (typeof clue === 'function') {
|
|
13
|
+
constructor = clue;
|
|
14
|
+
name = clue.name;
|
|
122
15
|
}
|
|
123
16
|
else {
|
|
124
|
-
constructor = undefined
|
|
17
|
+
constructor = undefined;
|
|
18
|
+
name = clue;
|
|
125
19
|
}
|
|
126
|
-
|
|
127
|
-
let item = SmartDI.resolved[name];
|
|
128
|
-
// Attempt to resolve by name.
|
|
129
|
-
if (!item)
|
|
130
|
-
item = SmartDI.registry[name];
|
|
131
|
-
// Attempt to resolve by constructor.
|
|
20
|
+
let item = SmartDI[$registry][name];
|
|
132
21
|
if (!item && constructor)
|
|
133
|
-
item = SmartDI.
|
|
134
|
-
// Throw an error if not resolved.
|
|
22
|
+
item = SmartDI.resovleOneByConstructor(constructor);
|
|
135
23
|
if (!item)
|
|
136
|
-
throw new Error(`Can not resolve
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
24
|
+
throw new Error(`Can not resolve injectable '${name}'!`);
|
|
25
|
+
return SmartDI.obtainInstance(item, creatings, ...args);
|
|
26
|
+
}
|
|
27
|
+
static internalGetArray(constructor, creatings, ...args) {
|
|
28
|
+
const items = SmartDI.resovleByConstructor(constructor);
|
|
29
|
+
if (items.length == 0)
|
|
30
|
+
throw new Error(`Can not resolve injectable '${constructor.name}'!`);
|
|
31
|
+
const instances = [];
|
|
32
|
+
for (const item of items)
|
|
33
|
+
instances.push(SmartDI.obtainInstance(item, creatings, ...args));
|
|
34
|
+
return instances;
|
|
35
|
+
}
|
|
36
|
+
static obtainInstance(item, creatings, ...args) {
|
|
37
|
+
if (item.instance)
|
|
38
|
+
return item.instance;
|
|
39
|
+
if (args.length == 0)
|
|
40
|
+
args = item.args;
|
|
41
|
+
const instance = new item.class(...args);
|
|
140
42
|
if (item.singleton)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', instance);
|
|
145
|
-
if (!autowireds)
|
|
43
|
+
item.instance = instance;
|
|
44
|
+
const injects = Reflect.getMetadata('smartdi:injects', instance);
|
|
45
|
+
if (!injects)
|
|
146
46
|
return instance;
|
|
147
|
-
|
|
148
|
-
creatings = creatings || new Set();
|
|
149
|
-
if (creatings.has(item.name))
|
|
47
|
+
if (!item.singleton && creatings.has(item.name))
|
|
150
48
|
throw new Error('Circular dependency detected!');
|
|
151
|
-
// Inject dependencies.
|
|
152
49
|
creatings.add(item.name);
|
|
153
|
-
for (
|
|
154
|
-
instance[prop] =
|
|
155
|
-
|
|
50
|
+
for (const prop of Object.keys(injects))
|
|
51
|
+
instance[prop] = injects[prop].array
|
|
52
|
+
? SmartDI.internalGetArray(injects[prop].clue, creatings, ...injects[prop].args)
|
|
53
|
+
: SmartDI.internalGet(injects[prop].clue, creatings, ...injects[prop].args);
|
|
156
54
|
return instance;
|
|
157
55
|
}
|
|
158
|
-
static
|
|
159
|
-
let matched = []
|
|
56
|
+
static resovleByConstructor(constructor) {
|
|
57
|
+
let matched = [];
|
|
160
58
|
function isSubclass(sub, sup) {
|
|
161
|
-
let proto = sub;
|
|
162
|
-
while (
|
|
163
|
-
if (
|
|
59
|
+
let proto = Object.getPrototypeOf(sub);
|
|
60
|
+
while (proto.name) {
|
|
61
|
+
if (proto === sup)
|
|
164
62
|
return true;
|
|
165
|
-
|
|
63
|
+
proto = Object.getPrototypeOf(sub);
|
|
166
64
|
}
|
|
167
65
|
return false;
|
|
168
66
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// Not an instance of specified class, continue.
|
|
172
|
-
if (!isSubclass(constructor, entry.class))
|
|
67
|
+
for (const item of Object.values(SmartDI[$registry])) {
|
|
68
|
+
if (!isSubclass(item.class, constructor))
|
|
173
69
|
continue;
|
|
174
|
-
|
|
175
|
-
matched.push(entry.name);
|
|
176
|
-
item = entry;
|
|
70
|
+
matched.push(item);
|
|
177
71
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
72
|
+
return matched;
|
|
73
|
+
}
|
|
74
|
+
static resovleOneByConstructor(constructor) {
|
|
75
|
+
const items = SmartDI.resovleByConstructor(constructor);
|
|
76
|
+
if (items.length > 1)
|
|
77
|
+
throw new Error(`Multiple injectable classes found for '${constructor.name}': ${items.map(item => item.name).join(', ')}`);
|
|
78
|
+
return items[0];
|
|
183
79
|
}
|
|
184
80
|
}
|
|
185
81
|
exports.SmartDI = SmartDI;
|
|
186
|
-
|
|
187
|
-
SmartDI
|
|
188
|
-
|
|
189
|
-
|
|
82
|
+
_a = $registry;
|
|
83
|
+
SmartDI[_a] = {};
|
|
84
|
+
function Injectable(options) {
|
|
85
|
+
return (target) => {
|
|
86
|
+
const name = (options === null || options === void 0 ? void 0 : options.name) || target.name;
|
|
87
|
+
const args = (options === null || options === void 0 ? void 0 : options.args) || [];
|
|
88
|
+
const singleton = !(options === null || options === void 0 ? void 0 : options.multiple);
|
|
89
|
+
if (SmartDI[$registry][name])
|
|
90
|
+
throw new Error(`Duplicated injectable name '${name}'!`);
|
|
91
|
+
SmartDI[$registry][name] = {
|
|
92
|
+
name,
|
|
93
|
+
class: target,
|
|
94
|
+
args: args,
|
|
95
|
+
singleton,
|
|
96
|
+
instance: undefined
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
exports.Injectable = Injectable;
|
|
101
|
+
function Inject(clue, ...args) {
|
|
102
|
+
return function (target, prop) {
|
|
103
|
+
let injects = Reflect.getMetadata('smartdi:injects', target) || {};
|
|
104
|
+
injects[prop] = { array: false, clue, args };
|
|
105
|
+
Reflect.defineMetadata('smartdi:injects', injects, target);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
exports.Inject = Inject;
|
|
109
|
+
function InjectArray(clue, ...args) {
|
|
110
|
+
return function (target, prop) {
|
|
111
|
+
let injects = Reflect.getMetadata('smartdi:injects', target) || {};
|
|
112
|
+
injects[prop] = { array: true, clue, args };
|
|
113
|
+
Reflect.defineMetadata('smartdi:injects', injects, target);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
exports.InjectArray = InjectArray;
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "smartdi",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "A simple dependency injector based on decorators.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"keywords": [
|
|
10
|
-
"DI",
|
|
11
|
-
"dependency",
|
|
12
|
-
"injector",
|
|
13
|
-
"decorator"
|
|
14
|
-
],
|
|
15
|
-
"author": "Eric.Tsai",
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "smartdi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A simple dependency injector based on decorators.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"DI",
|
|
11
|
+
"dependency",
|
|
12
|
+
"injector",
|
|
13
|
+
"decorator"
|
|
14
|
+
],
|
|
15
|
+
"author": "Eric.Tsai",
|
|
16
|
+
"files": [
|
|
17
|
+
"index.d.ts",
|
|
18
|
+
"index.js"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/iwares/smartdi.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/iwares/smartdi/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/iwares/smartdi#readme",
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.13.10",
|
|
31
|
+
"reflect-metadata": "^0.2.2"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.ts
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
import requireall from 'require-all'
|
|
2
|
-
import 'reflect-metadata'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Class.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
export type Class<T = any> = { new(arg?: any): T };
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* ComponentOptions.
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export type ComponentOptions = { [key: string]: any };
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Indicates that a decorated class is a "component". Such classes are considered as candidates for DI when scanning.
|
|
18
|
-
*
|
|
19
|
-
* @param name - name of this component(optional)
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
export function Component(options?: ComponentOptions, multiple?: boolean): ClassDecorator;
|
|
23
|
-
export function Component(name: string, multiple?: boolean): ClassDecorator;
|
|
24
|
-
export function Component(name: string, options: ComponentOptions, multiple?: boolean): ClassDecorator;
|
|
25
|
-
export function Component(arg1?: string | ComponentOptions, arg2?: boolean | ComponentOptions, arg3?: boolean): ClassDecorator {
|
|
26
|
-
return function (target: Function): void {
|
|
27
|
-
let name: string, singleton: boolean, options: ComponentOptions;
|
|
28
|
-
if (typeof arg1 === 'undefined' || typeof arg1 === 'object') {
|
|
29
|
-
name = target.name;
|
|
30
|
-
options = arg1;
|
|
31
|
-
singleton = !arg2;
|
|
32
|
-
} else if (typeof arg2 === 'boolean') {
|
|
33
|
-
name = arg1;
|
|
34
|
-
options = undefined;
|
|
35
|
-
singleton = !arg2;
|
|
36
|
-
} else {
|
|
37
|
-
name = arg1 as string;
|
|
38
|
-
options = arg2 as ComponentOptions;
|
|
39
|
-
singleton = !arg3;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
Reflect.defineMetadata('smartdi:component', name, target);
|
|
43
|
-
Reflect.defineMetadata('smartdi:singleton', singleton, target);
|
|
44
|
-
Reflect.defineMetadata('smartdi:options', options, target);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Marks a field to be autowired by SmartDI's dependency injection facilities.
|
|
50
|
-
*
|
|
51
|
-
* @param constructor - class or base class of the autowired component
|
|
52
|
-
* @param options - options used to creating new instances.
|
|
53
|
-
*
|
|
54
|
-
*/
|
|
55
|
-
export function Autowired(constructor: Class, options?: ComponentOptions): PropertyDecorator;
|
|
56
|
-
/**
|
|
57
|
-
* Marks a field to be autowired by SmartDI's dependency injection facilities.
|
|
58
|
-
*
|
|
59
|
-
* @param name - name of the autowired component
|
|
60
|
-
* @param options - options used to creating new instances.
|
|
61
|
-
*
|
|
62
|
-
*/
|
|
63
|
-
export function Autowired(name: string, options?: ComponentOptions): PropertyDecorator;
|
|
64
|
-
export function Autowired(arg1: Class | string, options?: ComponentOptions): PropertyDecorator {
|
|
65
|
-
return function (target: Object, prop: string | symbol): void {
|
|
66
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', target) || {};
|
|
67
|
-
autowireds[prop] = { clue: arg1, options: options };
|
|
68
|
-
Reflect.defineMetadata('smartdi:autowireds', autowireds, target);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Options to indicate how SmartDI wire up all components.
|
|
74
|
-
*
|
|
75
|
-
*/
|
|
76
|
-
export interface AutowireOptions {
|
|
77
|
-
/** built-in components */
|
|
78
|
-
components?: { [key: string]: any };
|
|
79
|
-
/** dirs to scan compoents */
|
|
80
|
-
scans?: Array<string>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface RegistryItem {
|
|
84
|
-
source: 'scan' | 'built-in';
|
|
85
|
-
name: string;
|
|
86
|
-
class: Class;
|
|
87
|
-
options: ComponentOptions;
|
|
88
|
-
singleton: any;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Main entry of SmartDI framework.
|
|
93
|
-
*
|
|
94
|
-
*/
|
|
95
|
-
export class SmartDI {
|
|
96
|
-
|
|
97
|
-
public static readonly version: '0.0.1';
|
|
98
|
-
|
|
99
|
-
private static readonly registry: { [key: string]: RegistryItem } = {};
|
|
100
|
-
private static readonly resolved: { [key: string]: RegistryItem } = {};
|
|
101
|
-
|
|
102
|
-
private static autowired: boolean = false;
|
|
103
|
-
|
|
104
|
-
private constructor() {
|
|
105
|
-
throw new Error('Do not instantiate this class.');
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Wire up all components according to specified options.
|
|
110
|
-
*
|
|
111
|
-
* @param options - autowire options
|
|
112
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
public static autowire(options: AutowireOptions): void {
|
|
115
|
-
if (SmartDI.autowired)
|
|
116
|
-
throw new Error('SmartDI.autowire() can only be called once.');
|
|
117
|
-
SmartDI.autowired = true;
|
|
118
|
-
|
|
119
|
-
for (let name of Object.keys(options.components || {})) {
|
|
120
|
-
let component = options.components![name];
|
|
121
|
-
SmartDI.registry[name] = {
|
|
122
|
-
source: 'built-in',
|
|
123
|
-
name: name,
|
|
124
|
-
class: component.constructor,
|
|
125
|
-
options: undefined,
|
|
126
|
-
singleton: component,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Iterate all dirs to find and instantiate all components.
|
|
131
|
-
for (let scan of options.scans || []) {
|
|
132
|
-
// Load all modules.
|
|
133
|
-
let modules = requireall({ dirname: scan, filter: /.+\.js$/ });
|
|
134
|
-
// Iterate all modules.
|
|
135
|
-
for (let module of Object.values(modules)) {
|
|
136
|
-
// Iterate all exported symbols
|
|
137
|
-
for (let symbol of Object.values<Class>(module)) {
|
|
138
|
-
// Not a function, ignore.
|
|
139
|
-
if (typeof symbol !== 'function')
|
|
140
|
-
continue;
|
|
141
|
-
|
|
142
|
-
// Fetch metadata, ignore if not found.
|
|
143
|
-
let name = Reflect.getMetadata('smartdi:component', symbol);
|
|
144
|
-
if (!name)
|
|
145
|
-
continue;
|
|
146
|
-
|
|
147
|
-
// Check conflicts
|
|
148
|
-
let exist = SmartDI.registry[name];
|
|
149
|
-
if (exist)
|
|
150
|
-
throw new Error(`Duplicate component name: ${name}`);
|
|
151
|
-
|
|
152
|
-
let singleton = Reflect.getMetadata('smartdi:singleton', symbol);
|
|
153
|
-
let options = Reflect.getMetadata('smartdi:options', symbol);
|
|
154
|
-
// Add to registry.
|
|
155
|
-
SmartDI.registry[name] = {
|
|
156
|
-
source: 'scan',
|
|
157
|
-
name: name,
|
|
158
|
-
class: symbol,
|
|
159
|
-
options: options,
|
|
160
|
-
singleton: singleton ? new symbol(options) : undefined,
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Iterate registry and inject dependencies.
|
|
167
|
-
for (let item of Object.values(SmartDI.registry)) {
|
|
168
|
-
// Fetch metadata;
|
|
169
|
-
if (!item.singleton)
|
|
170
|
-
continue;
|
|
171
|
-
|
|
172
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', item.singleton);
|
|
173
|
-
if (!autowireds)
|
|
174
|
-
continue;
|
|
175
|
-
|
|
176
|
-
// Inject components.
|
|
177
|
-
for (let prop of Object.keys(autowireds)) {
|
|
178
|
-
item.singleton[prop] = SmartDI.get(autowireds[prop].clue, autowireds[prop].options);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get a component which is a instance of the specified class.
|
|
186
|
-
*
|
|
187
|
-
* @param constructor - class or base class of the compoent
|
|
188
|
-
* @param options - options used to creating new instance.
|
|
189
|
-
*
|
|
190
|
-
*/
|
|
191
|
-
public static get<T>(constructor: Class<T>, options?: ComponentOptions): T;
|
|
192
|
-
/**
|
|
193
|
-
* Get a component by name.
|
|
194
|
-
*
|
|
195
|
-
* @param name - name of the compoent
|
|
196
|
-
* @param options - options used to creating new instance.
|
|
197
|
-
*
|
|
198
|
-
*/
|
|
199
|
-
public static get(name: string, options?: ComponentOptions): any;
|
|
200
|
-
public static get(arg: Class | string, options?: ComponentOptions): any {
|
|
201
|
-
return SmartDI.internalGet(arg, options);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private static internalGet(arg: Class | string, options: ComponentOptions, creatings?: Set<string>): any {
|
|
205
|
-
let constructor: Class | undefined, name: string;
|
|
206
|
-
if (typeof arg === 'function') {
|
|
207
|
-
constructor = arg, name = arg.name;
|
|
208
|
-
} else {
|
|
209
|
-
constructor = undefined, name = arg;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Find by name in resolved components.
|
|
213
|
-
let item = SmartDI.resolved[name];
|
|
214
|
-
|
|
215
|
-
// Attempt to resolve by name.
|
|
216
|
-
if (!item)
|
|
217
|
-
item = SmartDI.registry[name];
|
|
218
|
-
|
|
219
|
-
// Attempt to resolve by constructor.
|
|
220
|
-
if (!item && constructor)
|
|
221
|
-
item = SmartDI.resolveByConstructor(constructor);
|
|
222
|
-
|
|
223
|
-
// Throw an error if not resolved.
|
|
224
|
-
if (!item)
|
|
225
|
-
throw new Error(`Can not resolve component: ${name}`);
|
|
226
|
-
|
|
227
|
-
// Cache.
|
|
228
|
-
SmartDI.resolved[name] = item;
|
|
229
|
-
|
|
230
|
-
// Return singleton if exist.
|
|
231
|
-
if (item.singleton)
|
|
232
|
-
return item.singleton;
|
|
233
|
-
|
|
234
|
-
// Create a new instance.
|
|
235
|
-
let instance = new item.class(options || item.options);
|
|
236
|
-
let autowireds = Reflect.getMetadata('smartdi:autowireds', instance);
|
|
237
|
-
if (!autowireds)
|
|
238
|
-
return instance;
|
|
239
|
-
|
|
240
|
-
// Check cirrcular dependencies.
|
|
241
|
-
creatings = creatings || new Set();
|
|
242
|
-
if (creatings.has(item.name))
|
|
243
|
-
throw new Error('Circular dependency detected!');
|
|
244
|
-
|
|
245
|
-
// Inject dependencies.
|
|
246
|
-
creatings.add(item.name);
|
|
247
|
-
for (let prop of Object.keys(autowireds))
|
|
248
|
-
instance[prop] = SmartDI.internalGet(autowireds[prop].clue, autowireds[prop].options, creatings);
|
|
249
|
-
|
|
250
|
-
// Return instance.
|
|
251
|
-
return instance;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
private static resolveByConstructor(constructor: Class): RegistryItem {
|
|
255
|
-
let matched: Array<string> = [], item: any = undefined;
|
|
256
|
-
|
|
257
|
-
function isSubclass(sub: Class, sup: Class): boolean {
|
|
258
|
-
let proto = sub;
|
|
259
|
-
while (sub.name) {
|
|
260
|
-
if (sub === sup)
|
|
261
|
-
return true;
|
|
262
|
-
sub = (sub as any).__proto__;
|
|
263
|
-
}
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Iterate registry
|
|
268
|
-
for (let entry of Object.values(SmartDI.registry)) {
|
|
269
|
-
// Not an instance of specified class, continue.
|
|
270
|
-
if (!isSubclass(constructor, entry.class))
|
|
271
|
-
continue;
|
|
272
|
-
|
|
273
|
-
// Record resolved name and instance.
|
|
274
|
-
matched.push(entry.name);
|
|
275
|
-
item = entry;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Throw an error when matched more then on components.
|
|
279
|
-
if (matched.length > 1)
|
|
280
|
-
throw new Error(`Multiple components found for "${constructor.name}": ${matched.join(', ')}`);
|
|
281
|
-
|
|
282
|
-
// return result.
|
|
283
|
-
return item;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2016",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"sourceMap": true,
|
|
7
|
-
"moduleResolution": "node",
|
|
8
|
-
"esModuleInterop": true,
|
|
9
|
-
"experimentalDecorators": true,
|
|
10
|
-
"emitDecoratorMetadata": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true
|
|
13
|
-
}
|
|
14
|
-
}
|