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 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
- import 'reflect-metadata';
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
- * ComponentOptions.
11
- *
12
- */
13
- export declare type ComponentOptions = {
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
- * Main entry of SmartDI framework.
55
- *
56
- */
57
- export declare class SmartDI {
58
- static readonly version: '0.0.1';
59
- private static readonly registry;
60
- private static readonly resolved;
61
- private static autowired;
62
- private constructor();
63
- /**
64
- * Wire up all components according to specified options.
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 resolveByConstructor;
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 __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ var _a;
5
3
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SmartDI = exports.Autowired = exports.Component = void 0;
7
- const require_all_1 = __importDefault(require("require-all"));
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
- constructor() {
47
- throw new Error('Do not instantiate this class.');
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 get(arg, options) {
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 arg === 'function') {
121
- constructor = arg, name = arg.name;
12
+ if (typeof clue === 'function') {
13
+ constructor = clue;
14
+ name = clue.name;
122
15
  }
123
16
  else {
124
- constructor = undefined, name = arg;
17
+ constructor = undefined;
18
+ name = clue;
125
19
  }
126
- // Find by name in resolved components.
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.resolveByConstructor(constructor);
134
- // Throw an error if not resolved.
22
+ item = SmartDI.resovleOneByConstructor(constructor);
135
23
  if (!item)
136
- throw new Error(`Can not resolve component: ${name}`);
137
- // Cache.
138
- SmartDI.resolved[name] = item;
139
- // Return singleton if exist.
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
- return item.singleton;
142
- // Create a new instance.
143
- let instance = new item.class(options || item.options);
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
- // Check cirrcular dependencies.
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 (let prop of Object.keys(autowireds))
154
- instance[prop] = SmartDI.internalGet(autowireds[prop].clue, autowireds[prop].options, creatings);
155
- // Return instance.
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 resolveByConstructor(constructor) {
159
- let matched = [], item = undefined;
56
+ static resovleByConstructor(constructor) {
57
+ let matched = [];
160
58
  function isSubclass(sub, sup) {
161
- let proto = sub;
162
- while (sub.name) {
163
- if (sub === sup)
59
+ let proto = Object.getPrototypeOf(sub);
60
+ while (proto.name) {
61
+ if (proto === sup)
164
62
  return true;
165
- sub = sub.__proto__;
63
+ proto = Object.getPrototypeOf(sub);
166
64
  }
167
65
  return false;
168
66
  }
169
- // Iterate registry
170
- for (let entry of Object.values(SmartDI.registry)) {
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
- // Record resolved name and instance.
175
- matched.push(entry.name);
176
- item = entry;
70
+ matched.push(item);
177
71
  }
178
- // Throw an error when matched more then on components.
179
- if (matched.length > 1)
180
- throw new Error(`Multiple components found for "${constructor.name}": ${matched.join(', ')}`);
181
- // return result.
182
- return item;
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
- SmartDI.registry = {};
187
- SmartDI.resolved = {};
188
- SmartDI.autowired = false;
189
- //# sourceMappingURL=index.js.map
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.1",
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
- "license": "MIT",
17
- "devDependencies": {
18
- "@types/node": "^15.0.1",
19
- "@types/require-all": "^3.0.1"
20
- },
21
- "dependencies": {
22
- "reflect-metadata": "^0.1.13",
23
- "require-all": "^3.0.0"
24
- },
25
- "repository": {
26
- "type": "git",
27
- "url": "git+https://github.com/iwares/simpledi.git"
28
- },
29
- "bugs": {
30
- "url": "https://github.com/iwares/simpledi/issues"
31
- },
32
- "homepage": "https://github.com/iwares/simpledi#readme"
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
- }