@travetto/di 6.0.0-rc.0 → 6.0.0-rc.2
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 +12 -13
- package/__index__.ts +4 -5
- package/package.json +3 -3
- package/src/decorator.ts +2 -2
- package/src/error.ts +1 -1
- package/src/registry.ts +43 -30
- package/src/types.ts +27 -24
- package/support/dynamic.injection.ts +9 -9
- package/support/test/suite.ts +1 -1
- package/support/transformer.injectable.ts +21 -46
- package/src/global.d.ts +0 -27
- package/src/internal/types.ts +0 -1
package/README.md
CHANGED
|
@@ -43,9 +43,9 @@ class CustomService {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const
|
|
46
|
+
const Custom2Symbol = Symbol.for('di-custom2');
|
|
47
47
|
|
|
48
|
-
@Injectable({ target: CustomService, qualifier:
|
|
48
|
+
@Injectable({ target: CustomService, qualifier: Custom2Symbol })
|
|
49
49
|
class CustomService2 extends CustomService {
|
|
50
50
|
override async coolOperation() {
|
|
51
51
|
await super.coolOperation();
|
|
@@ -54,7 +54,7 @@ class CustomService2 extends CustomService {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
class Consumer {
|
|
57
|
-
@Inject(
|
|
57
|
+
@Inject(Custom2Symbol) // Pull in specific service
|
|
58
58
|
service: CustomService;
|
|
59
59
|
}
|
|
60
60
|
```
|
|
@@ -125,7 +125,7 @@ class RuntimeService {
|
|
|
125
125
|
|
|
126
126
|
In this example, the enabled flag is specified in relationship to the deployment environment. When coupled with optional properties, and optional chaining, allows for seamless inclusion of optional dependencies at runtime.
|
|
127
127
|
|
|
128
|
-
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L29) that also provide additional functionality. For example, the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support") module @Config or the [
|
|
128
|
+
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L29) that also provide additional functionality. For example, the [Configuration](https://github.com/travetto/travetto/tree/main/module/config#readme "Configuration support") module @Config or the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") module @Controller decorator registers the associated class as an injectable element.
|
|
129
129
|
|
|
130
130
|
## Injection
|
|
131
131
|
Once all of your necessary dependencies are defined, now is the time to provide those [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L29) instances to your code. There are three primary methods for injection:
|
|
@@ -135,7 +135,7 @@ The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decor
|
|
|
135
135
|
**Code: Example Injectable with dependencies as Inject fields**
|
|
136
136
|
```typescript
|
|
137
137
|
import { Injectable, Inject } from '@travetto/di';
|
|
138
|
-
import { DependentService } from './dep';
|
|
138
|
+
import { DependentService } from './dep.ts';
|
|
139
139
|
|
|
140
140
|
@Injectable()
|
|
141
141
|
class CustomService {
|
|
@@ -153,7 +153,7 @@ The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/d
|
|
|
153
153
|
**Code: Example Injectable with dependencies in constructor**
|
|
154
154
|
```typescript
|
|
155
155
|
import { Injectable } from '@travetto/di';
|
|
156
|
-
import { DependentService } from './dep';
|
|
156
|
+
import { DependentService } from './dep.ts';
|
|
157
157
|
|
|
158
158
|
@Injectable()
|
|
159
159
|
class CustomService {
|
|
@@ -176,7 +176,7 @@ Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/d
|
|
|
176
176
|
```typescript
|
|
177
177
|
import { InjectableFactory } from '@travetto/di';
|
|
178
178
|
|
|
179
|
-
import { DependentService, CustomService } from './dep';
|
|
179
|
+
import { DependentService, CustomService } from './dep.ts';
|
|
180
180
|
|
|
181
181
|
class Config {
|
|
182
182
|
@InjectableFactory()
|
|
@@ -216,7 +216,7 @@ By default, if there is only one candidate without qualification, then that cand
|
|
|
216
216
|
**Code: Example Multiple Candidate Types**
|
|
217
217
|
```typescript
|
|
218
218
|
import { InjectableFactory } from '@travetto/di';
|
|
219
|
-
import { Contract, ComplexContract } from './injectable-multiple-default';
|
|
219
|
+
import { Contract, ComplexContract } from './injectable-multiple-default.ts';
|
|
220
220
|
|
|
221
221
|
class Config {
|
|
222
222
|
// Complex will be marked as the available Contract
|
|
@@ -268,7 +268,7 @@ class Service {
|
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
## Manual Invocation
|
|
271
|
-
Some times you will need to lookup a dependency dynamically, or you want to control the injection process at a more granular level. To achieve that you will need to directly access the [DependencyRegistry](https://github.com/travetto/travetto/tree/main/module/di/src/registry.ts#
|
|
271
|
+
Some times you will need to lookup a dependency dynamically, or you want to control the injection process at a more granular level. To achieve that you will need to directly access the [DependencyRegistry](https://github.com/travetto/travetto/tree/main/module/di/src/registry.ts#L25). The registry allows for requesting a dependency by class reference:
|
|
272
272
|
|
|
273
273
|
**Code: Example of Manual Lookup**
|
|
274
274
|
```typescript
|
|
@@ -290,11 +290,10 @@ Additionally, support for interfaces (over class inheritance) is provided, but r
|
|
|
290
290
|
**Code: Example Interface Injection**
|
|
291
291
|
```typescript
|
|
292
292
|
import { DependencyRegistry, Inject, Injectable, InjectableFactory } from '@travetto/di';
|
|
293
|
-
|
|
294
|
-
class TargetConcrete { }
|
|
293
|
+
import { toConcrete } from '@travetto/runtime';
|
|
295
294
|
|
|
296
295
|
/**
|
|
297
|
-
* @concrete
|
|
296
|
+
* @concrete
|
|
298
297
|
*/
|
|
299
298
|
export interface ServiceContract {
|
|
300
299
|
deleteUser(userId: string): Promise<void>;
|
|
@@ -316,7 +315,7 @@ class SpecificService {
|
|
|
316
315
|
class ManualInvocationOfInterface {
|
|
317
316
|
@InjectableFactory()
|
|
318
317
|
static getCustomService(): Promise<ServiceContract> {
|
|
319
|
-
return DependencyRegistry.getInstance<ServiceContract>(
|
|
318
|
+
return DependencyRegistry.getInstance<ServiceContract>(toConcrete<ServiceContract>());
|
|
320
319
|
}
|
|
321
320
|
}
|
|
322
321
|
```
|
package/__index__.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export * from './src/
|
|
3
|
-
export * from './src/
|
|
4
|
-
export * from './src/
|
|
5
|
-
export * from './src/types';
|
|
1
|
+
export * from './src/decorator.ts';
|
|
2
|
+
export * from './src/error.ts';
|
|
3
|
+
export * from './src/registry.ts';
|
|
4
|
+
export * from './src/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/di",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.2",
|
|
4
4
|
"description": "Dependency registration/management and injection support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast-transformations",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"directory": "module/di"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/registry": "^6.0.0-rc.
|
|
30
|
+
"@travetto/registry": "^6.0.0-rc.2"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@travetto/transformer": "^6.0.0-rc.
|
|
33
|
+
"@travetto/transformer": "^6.0.0-rc.3"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
36
36
|
"@travetto/transformer": {
|
package/src/decorator.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { asConstructable, asFull, TypedFunction, type Class } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { InjectableFactoryConfig, InjectableConfig, Dependency } from './types';
|
|
4
|
-
import { DependencyRegistry, ResolutionType } from './registry';
|
|
3
|
+
import { InjectableFactoryConfig, InjectableConfig, Dependency } from './types.ts';
|
|
4
|
+
import { DependencyRegistry, ResolutionType } from './registry.ts';
|
|
5
5
|
|
|
6
6
|
function collapseConfig<T extends { qualifier?: symbol }>(...args: (symbol | Partial<InjectConfig> | undefined)[]): T {
|
|
7
7
|
let out: Partial<T> = {};
|
package/src/error.ts
CHANGED
package/src/registry.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Class, Runtime, asConstructable, castTo, classConstruct, describeFunction,
|
|
3
|
-
asFull, castKey, TypedFunction, hasFunction
|
|
3
|
+
asFull, castKey, TypedFunction, hasFunction, AppError
|
|
4
4
|
} from '@travetto/runtime';
|
|
5
5
|
import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry';
|
|
6
6
|
|
|
7
|
-
import { Dependency, InjectableConfig, ClassTarget, InjectableFactoryConfig } from './types';
|
|
8
|
-
import { InjectionError } from './error';
|
|
9
|
-
import { AutoCreateTarget } from './internal/types';
|
|
7
|
+
import { Dependency, InjectableConfig, ClassTarget, InjectableFactoryConfig, PostConstructHandler } from './types.ts';
|
|
8
|
+
import { InjectionError } from './error.ts';
|
|
10
9
|
|
|
10
|
+
class AutoCreate { }
|
|
11
11
|
type TargetId = string;
|
|
12
12
|
type ClassId = string;
|
|
13
13
|
export type Resolved<T> = { config: InjectableConfig<T>, qualifier: symbol, id: string };
|
|
14
14
|
|
|
15
15
|
export type ResolutionType = 'strict' | 'loose' | 'any';
|
|
16
16
|
|
|
17
|
-
const PrimaryCandidateSymbol = Symbol
|
|
17
|
+
const PrimaryCandidateSymbol = Symbol();
|
|
18
18
|
|
|
19
19
|
const hasPostConstruct = hasFunction<{ postConstruct: () => Promise<unknown> }>('postConstruct');
|
|
20
20
|
const hasPreDestroy = hasFunction<{ preDestroy: () => Promise<unknown> }>('preDestroy');
|
|
@@ -104,12 +104,12 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
104
104
|
/**
|
|
105
105
|
* Retrieve all dependencies
|
|
106
106
|
*/
|
|
107
|
-
async fetchDependencies(managed: InjectableConfig
|
|
107
|
+
async fetchDependencies<T>(managed: InjectableConfig<T>, deps?: Dependency[], keys?: string[]): Promise<unknown[]> {
|
|
108
108
|
if (!deps || !deps.length) {
|
|
109
109
|
return [];
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const promises = deps.map(async x => {
|
|
112
|
+
const promises = deps.map(async (x, i) => {
|
|
113
113
|
try {
|
|
114
114
|
return await this.getInstance(x.target, x.qualifier, x.resolution);
|
|
115
115
|
} catch (err) {
|
|
@@ -117,7 +117,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
117
117
|
return undefined;
|
|
118
118
|
} else {
|
|
119
119
|
if (err && err instanceof Error) {
|
|
120
|
-
err.message = `${err.message} via=${managed.class.Ⲑid}`;
|
|
120
|
+
err.message = `${err.message} via=${managed.class.Ⲑid}[${keys?.[i] ?? 'constructor'}]`;
|
|
121
121
|
}
|
|
122
122
|
throw err;
|
|
123
123
|
}
|
|
@@ -136,7 +136,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
136
136
|
|
|
137
137
|
// And auto-wire
|
|
138
138
|
if (keys.length) {
|
|
139
|
-
const deps = await this.fetchDependencies(config, keys.map(x => config.dependencies.fields[x]));
|
|
139
|
+
const deps = await this.fetchDependencies(config, keys.map(x => config.dependencies.fields[x]), keys);
|
|
140
140
|
for (let i = 0; i < keys.length; i++) {
|
|
141
141
|
instance[castKey<T>(keys[i])] = castTo(deps[i]);
|
|
142
142
|
}
|
|
@@ -174,6 +174,11 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
174
174
|
await inst.postConstruct();
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
// Run post constructors
|
|
178
|
+
for (const op of Object.values(managed.postConstruct)) {
|
|
179
|
+
await op(inst);
|
|
180
|
+
}
|
|
181
|
+
|
|
177
182
|
return inst;
|
|
178
183
|
}
|
|
179
184
|
|
|
@@ -226,13 +231,11 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
226
231
|
override async init(): Promise<void> {
|
|
227
232
|
await super.init();
|
|
228
233
|
if (Runtime.dynamic) {
|
|
229
|
-
const { DependencyRegistration } = await import('../support/dynamic.injection');
|
|
234
|
+
const { DependencyRegistration } = await import('../support/dynamic.injection.ts');
|
|
230
235
|
DependencyRegistration.init(this);
|
|
231
236
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
await this.getInstance(cfg.class, cfg.qualifier);
|
|
235
|
-
}
|
|
237
|
+
|
|
238
|
+
await this.getCandidateInstances(AutoCreate);
|
|
236
239
|
}
|
|
237
240
|
|
|
238
241
|
/**
|
|
@@ -265,7 +268,8 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
265
268
|
dependencies: {
|
|
266
269
|
fields: {},
|
|
267
270
|
cons: []
|
|
268
|
-
}
|
|
271
|
+
},
|
|
272
|
+
postConstruct: {}
|
|
269
273
|
};
|
|
270
274
|
}
|
|
271
275
|
|
|
@@ -275,6 +279,10 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
275
279
|
async getInstance<T>(target: ClassTarget<T>, qual?: symbol, resolution?: ResolutionType): Promise<T> {
|
|
276
280
|
this.verifyInitialized();
|
|
277
281
|
|
|
282
|
+
if (!target) {
|
|
283
|
+
throw new AppError('Unable to get instance when target is undefined');
|
|
284
|
+
}
|
|
285
|
+
|
|
278
286
|
const { id: classId, qualifier } = this.resolveTarget(target, qual, resolution);
|
|
279
287
|
if (!this.instances.has(classId) || !this.instances.get(classId)!.has(qualifier)) {
|
|
280
288
|
await this.createInstance(target, qualifier); // Wait for proxy
|
|
@@ -285,7 +293,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
285
293
|
/**
|
|
286
294
|
* Get all available candidate types for the target
|
|
287
295
|
*/
|
|
288
|
-
getCandidateTypes<T
|
|
296
|
+
getCandidateTypes<T>(target: Class<T>): InjectableConfig<T>[] {
|
|
289
297
|
const qualifiers = this.targetToClass.get(target.Ⲑid)!;
|
|
290
298
|
const uniqueQualifiers = qualifiers ? Array.from(new Set(qualifiers.values())) : [];
|
|
291
299
|
return castTo(uniqueQualifiers.map(id => this.get(id)));
|
|
@@ -294,7 +302,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
294
302
|
/**
|
|
295
303
|
* Get candidate instances by target type, with an optional filter
|
|
296
304
|
*/
|
|
297
|
-
getCandidateInstances<T>(target: Class
|
|
305
|
+
getCandidateInstances<T>(target: Class<T>, predicate?: (cfg: InjectableConfig<T>) => boolean): Promise<T[]> {
|
|
298
306
|
const inputs = this.getCandidateTypes<T>(target).filter(x => !predicate || predicate(x));
|
|
299
307
|
return Promise.all(inputs.map(l => this.getInstance<T>(l.class, l.qualifier)));
|
|
300
308
|
}
|
|
@@ -307,6 +315,14 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
307
315
|
conf.dependencies!.cons = dependencies;
|
|
308
316
|
}
|
|
309
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Register a post construct handler
|
|
320
|
+
*/
|
|
321
|
+
registerPostConstructHandler<T>(cls: Class<T>, name: string, handler: PostConstructHandler<T>): void {
|
|
322
|
+
const conf = this.getOrCreatePending(cls);
|
|
323
|
+
conf.postConstruct![name] = castTo(handler);
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
/**
|
|
311
327
|
* Register a property as a dependency
|
|
312
328
|
*/
|
|
@@ -325,7 +341,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
325
341
|
config.class = cls;
|
|
326
342
|
config.qualifier = pConfig.qualifier ?? config.qualifier ?? Symbol.for(cls.Ⲑid);
|
|
327
343
|
if (pConfig.interfaces) {
|
|
328
|
-
config.interfaces
|
|
344
|
+
(config.interfaces ??= []).push(...pConfig.interfaces);
|
|
329
345
|
}
|
|
330
346
|
if (pConfig.primary !== undefined) {
|
|
331
347
|
config.primary = pConfig.primary;
|
|
@@ -344,6 +360,9 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
344
360
|
}
|
|
345
361
|
};
|
|
346
362
|
}
|
|
363
|
+
if (pConfig.autoCreate) {
|
|
364
|
+
(config.interfaces ??= []).push(AutoCreate);
|
|
365
|
+
}
|
|
347
366
|
}
|
|
348
367
|
|
|
349
368
|
/**
|
|
@@ -412,7 +431,7 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
412
431
|
|
|
413
432
|
const config: InjectableConfig<T> = castTo(this.getOrCreatePending(cls));
|
|
414
433
|
|
|
415
|
-
if (!(typeof config.enabled === 'boolean' ? config.enabled : config.enabled())) {
|
|
434
|
+
if (config.enabled !== undefined && !(typeof config.enabled === 'boolean' ? config.enabled : config.enabled())) {
|
|
416
435
|
return config; // Do not setup if disabled
|
|
417
436
|
}
|
|
418
437
|
|
|
@@ -444,6 +463,11 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
444
463
|
...config.interfaces
|
|
445
464
|
];
|
|
446
465
|
|
|
466
|
+
config.postConstruct = {
|
|
467
|
+
...parentConfig.postConstruct,
|
|
468
|
+
...config.postConstruct
|
|
469
|
+
};
|
|
470
|
+
|
|
447
471
|
// Inherit cons deps if no constructor defined
|
|
448
472
|
if (config.dependencies.cons === undefined) {
|
|
449
473
|
config.dependencies.cons = parentConfig.dependencies.cons;
|
|
@@ -550,17 +574,6 @@ class $DependencyRegistry extends MetadataRegistry<InjectableConfig> {
|
|
|
550
574
|
// Compute fields to be auto-wired
|
|
551
575
|
return await this.resolveFieldDependencies(this.get(cls), o);
|
|
552
576
|
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Execute the run method of a given class
|
|
556
|
-
*/
|
|
557
|
-
async runInstance<T extends { run(..._args: unknown[]): unknown }>(
|
|
558
|
-
cls: Class<T>, ...args: Parameters<T['run']>
|
|
559
|
-
): Promise<Awaited<ReturnType<T['run']>>> {
|
|
560
|
-
await RootRegistry.init();
|
|
561
|
-
const inst = await this.getInstance<T>(cls);
|
|
562
|
-
return castTo<Awaited<ReturnType<T['run']>>>(inst.run(...args));
|
|
563
|
-
}
|
|
564
577
|
}
|
|
565
578
|
|
|
566
579
|
export const DependencyRegistry = new $DependencyRegistry();
|
package/src/types.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Class } from '@travetto/runtime';
|
|
|
2
2
|
|
|
3
3
|
export type ClassTarget<T = unknown> = Class<T> | Function;
|
|
4
4
|
|
|
5
|
+
export type PostConstructHandler<T> = (value: T) => (void | Promise<void>);
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* State of a Dependency
|
|
7
9
|
*/
|
|
@@ -16,6 +18,24 @@ interface Core<T = unknown> {
|
|
|
16
18
|
qualifier: symbol;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* State of a Dependency Target
|
|
23
|
+
*/
|
|
24
|
+
interface CoreTarget<T = unknown> extends Core<T> {
|
|
25
|
+
/**
|
|
26
|
+
* Is this injectable enabled
|
|
27
|
+
*/
|
|
28
|
+
enabled?: boolean | (() => boolean);
|
|
29
|
+
/**
|
|
30
|
+
* Is this the primary instance
|
|
31
|
+
*/
|
|
32
|
+
primary?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Should the target be auto-created
|
|
35
|
+
*/
|
|
36
|
+
autoCreate?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
/**
|
|
20
40
|
* State of a Dependency
|
|
21
41
|
*/
|
|
@@ -35,11 +55,7 @@ export interface Dependency<T = unknown> extends Core<T> {
|
|
|
35
55
|
/**
|
|
36
56
|
* Injectable configuration
|
|
37
57
|
*/
|
|
38
|
-
export interface InjectableConfig<T = unknown> extends
|
|
39
|
-
/**
|
|
40
|
-
* Is this injectable enabled
|
|
41
|
-
*/
|
|
42
|
-
enabled: boolean | (() => boolean);
|
|
58
|
+
export interface InjectableConfig<T = unknown> extends CoreTarget<T> {
|
|
43
59
|
/**
|
|
44
60
|
* Reference for the class
|
|
45
61
|
*/
|
|
@@ -48,10 +64,6 @@ export interface InjectableConfig<T = unknown> extends Core<T> {
|
|
|
48
64
|
* Factory function for the injectable
|
|
49
65
|
*/
|
|
50
66
|
factory?: (...args: unknown[]) => T;
|
|
51
|
-
/**
|
|
52
|
-
* Is this the primary instance
|
|
53
|
-
*/
|
|
54
|
-
primary: boolean;
|
|
55
67
|
/**
|
|
56
68
|
* List of dependencies as fields or as constructor arguments
|
|
57
69
|
*/
|
|
@@ -63,31 +75,22 @@ export interface InjectableConfig<T = unknown> extends Core<T> {
|
|
|
63
75
|
* List of interface types
|
|
64
76
|
*/
|
|
65
77
|
interfaces: Class[];
|
|
78
|
+
/**
|
|
79
|
+
* Post construct handlers
|
|
80
|
+
*/
|
|
81
|
+
postConstruct: Record<string | symbol, PostConstructHandler<unknown>>;
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
/**
|
|
69
85
|
* Factory configuration
|
|
70
86
|
*/
|
|
71
|
-
export interface InjectableFactoryConfig<T = unknown> extends
|
|
72
|
-
/**
|
|
73
|
-
* Is this injectable enabled
|
|
74
|
-
*/
|
|
75
|
-
enabled?: boolean | (() => boolean);
|
|
87
|
+
export interface InjectableFactoryConfig<T = unknown> extends CoreTarget<T> {
|
|
76
88
|
/**
|
|
77
89
|
* Src of the factory method
|
|
78
90
|
*/
|
|
79
91
|
src: Class<T>;
|
|
80
|
-
/**
|
|
81
|
-
* Is this the primary instance
|
|
82
|
-
*/
|
|
83
|
-
primary?: boolean;
|
|
84
92
|
/**
|
|
85
93
|
* List of all dependencies as function arguments
|
|
86
94
|
*/
|
|
87
95
|
dependencies?: Dependency[];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* @concrete ./internal/types#AutoCreateTarget
|
|
92
|
-
*/
|
|
93
|
-
export interface AutoCreate { }
|
|
96
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Class, describeFunction } from '@travetto/runtime';
|
|
2
2
|
import { RetargettingProxy } from '@travetto/registry';
|
|
3
3
|
|
|
4
|
-
import type { DependencyRegistry, ResolutionType, Resolved } from '../src/registry';
|
|
5
|
-
import type { ClassTarget, InjectableConfig } from '../src/types';
|
|
4
|
+
import type { DependencyRegistry, ResolutionType, Resolved } from '../src/registry.ts';
|
|
5
|
+
import type { ClassTarget, InjectableConfig } from '../src/types.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Wraps the Dependency Registry to support proxying instances
|
|
@@ -89,14 +89,14 @@ class $DynamicDependencyRegistry {
|
|
|
89
89
|
|
|
90
90
|
register(registry: typeof DependencyRegistry): void {
|
|
91
91
|
this.#registry = registry;
|
|
92
|
-
this.#registryCreateInstance = registry
|
|
93
|
-
this.#registryResolveTarget = registry
|
|
94
|
-
this.#registryOnInstallFinalize = registry
|
|
95
|
-
this.#registryDestroyInstance = registry
|
|
92
|
+
this.#registryCreateInstance = registry.createInstance.bind(registry);
|
|
93
|
+
this.#registryResolveTarget = registry.resolveTarget.bind(registry);
|
|
94
|
+
this.#registryOnInstallFinalize = registry.onInstallFinalize.bind(registry);
|
|
95
|
+
this.#registryDestroyInstance = registry.destroyInstance.bind(registry);
|
|
96
96
|
|
|
97
|
-
this.#registry
|
|
98
|
-
this.#registry
|
|
99
|
-
this.#registry
|
|
97
|
+
this.#registry.createInstance = this.createInstance.bind(this);
|
|
98
|
+
this.#registry.destroyInstance = this.destroyInstance.bind(this);
|
|
99
|
+
this.#registry.onInstallFinalize = this.onInstallFinalize.bind(this);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
package/support/test/suite.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { castTo, Class, ClassInstance } from '@travetto/runtime';
|
|
|
2
2
|
import { RootRegistry } from '@travetto/registry';
|
|
3
3
|
import { SuiteRegistry } from '@travetto/test';
|
|
4
4
|
|
|
5
|
-
import { DependencyRegistry } from '../../src/registry';
|
|
5
|
+
import { DependencyRegistry } from '../../src/registry.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Registers a suite as injectable
|
|
@@ -1,33 +1,19 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, OnSetter
|
|
5
|
-
} from '@travetto/transformer';
|
|
6
|
-
import { ForeignType } from '@travetto/transformer/src/resolver/types';
|
|
3
|
+
import { TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, OnSetter } from '@travetto/transformer';
|
|
7
4
|
|
|
8
|
-
const
|
|
5
|
+
const INJECTABLE_IMPORT = '@travetto/di/src/decorator.ts';
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* Injectable/Injection transformer
|
|
12
9
|
*/
|
|
13
10
|
export class InjectableTransformer {
|
|
14
11
|
|
|
15
|
-
static getForeignTarget(state: TransformerState, ret: ForeignType): ts.Expression {
|
|
16
|
-
return state.fromLiteral({
|
|
17
|
-
Ⲑid: `${ret.source.split('node_modules/')[1]}+${ret.name}`
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
/**
|
|
22
13
|
* Handle a specific declaration param/property
|
|
23
14
|
*/
|
|
24
15
|
static processDeclaration(state: TransformerState, param: ts.ParameterDeclaration | ts.SetAccessorDeclaration | ts.PropertyDeclaration): ts.Expression[] {
|
|
25
|
-
const existing = state.findDecorator(this, param, 'Inject',
|
|
26
|
-
|
|
27
|
-
if (!(existing || ts.isParameter(param))) {
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
|
|
16
|
+
const existing = state.findDecorator(this, param, 'Inject', INJECTABLE_IMPORT);
|
|
31
17
|
const args: ts.Expression[] = [];
|
|
32
18
|
|
|
33
19
|
if (existing && ts.isCallExpression(existing.expression)) {
|
|
@@ -41,18 +27,7 @@ export class InjectableTransformer {
|
|
|
41
27
|
}
|
|
42
28
|
|
|
43
29
|
const keyParam = ts.isSetAccessorDeclaration(param) ? param.parameters[0] : param;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (type.key === 'managed') {
|
|
47
|
-
payload.target = state.getOrImport(type);
|
|
48
|
-
} else if (type.key === 'foreign') {
|
|
49
|
-
payload.target = this.getForeignTarget(state, type);
|
|
50
|
-
} else {
|
|
51
|
-
const file = param.getSourceFile().fileName;
|
|
52
|
-
const src = state.getFileImportName(file);
|
|
53
|
-
throw new Error(`Unable to import non-external type: ${param.getText()} ${type.key}: ${src}`);
|
|
54
|
-
}
|
|
55
|
-
|
|
30
|
+
payload.target = state.getConcreteType(keyParam);
|
|
56
31
|
args.unshift(state.fromLiteral(payload));
|
|
57
32
|
|
|
58
33
|
return args;
|
|
@@ -82,15 +57,15 @@ export class InjectableTransformer {
|
|
|
82
57
|
}
|
|
83
58
|
|
|
84
59
|
// Add injectable decorator if not there
|
|
85
|
-
const decl = state.findDecorator(this, node, 'Injectable',
|
|
60
|
+
const decl = state.findDecorator(this, node, 'Injectable', INJECTABLE_IMPORT);
|
|
86
61
|
const args = decl && ts.isCallExpression(decl.expression) ? decl.expression.arguments : [undefined];
|
|
87
62
|
|
|
88
63
|
return state.factory.updateClassDeclaration(node,
|
|
89
64
|
DecoratorUtil.spliceDecorators(node, decl, [
|
|
90
|
-
state.createDecorator(
|
|
65
|
+
state.createDecorator(INJECTABLE_IMPORT, 'Injectable', ...args, LiteralUtil.extendObjectLiteral(ts.factory, {}, {
|
|
91
66
|
interfaces
|
|
92
67
|
})),
|
|
93
|
-
state.createDecorator(
|
|
68
|
+
state.createDecorator(INJECTABLE_IMPORT, 'InjectArgs', injectArgs)
|
|
94
69
|
]),
|
|
95
70
|
node.name,
|
|
96
71
|
node.typeParameters,
|
|
@@ -103,14 +78,14 @@ export class InjectableTransformer {
|
|
|
103
78
|
* Handle Inject annotations for fields/args
|
|
104
79
|
*/
|
|
105
80
|
@OnProperty('Inject')
|
|
106
|
-
static registerInjectProperty(state: TransformerState, node: ts.PropertyDeclaration
|
|
107
|
-
const decl = state.findDecorator(this, node, 'Inject',
|
|
81
|
+
static registerInjectProperty(state: TransformerState, node: ts.PropertyDeclaration): typeof node {
|
|
82
|
+
const decl = state.findDecorator(this, node, 'Inject', INJECTABLE_IMPORT);
|
|
108
83
|
|
|
109
84
|
// Doing decls
|
|
110
85
|
return state.factory.updatePropertyDeclaration(
|
|
111
86
|
node,
|
|
112
87
|
DecoratorUtil.spliceDecorators(node, decl, [
|
|
113
|
-
state.createDecorator(
|
|
88
|
+
state.createDecorator(INJECTABLE_IMPORT, 'Inject', ...this.processDeclaration(state, node)),
|
|
114
89
|
], 0),
|
|
115
90
|
node.name,
|
|
116
91
|
node.questionToken,
|
|
@@ -123,11 +98,11 @@ export class InjectableTransformer {
|
|
|
123
98
|
* Handle Inject annotations for fields/args
|
|
124
99
|
*/
|
|
125
100
|
@OnSetter('Inject')
|
|
126
|
-
static registerInjectSetter(state: TransformerState, node: ts.SetAccessorDeclaration
|
|
127
|
-
const decl = state.findDecorator(this, node, 'Inject',
|
|
101
|
+
static registerInjectSetter(state: TransformerState, node: ts.SetAccessorDeclaration): typeof node {
|
|
102
|
+
const decl = state.findDecorator(this, node, 'Inject', INJECTABLE_IMPORT);
|
|
128
103
|
|
|
129
104
|
const modifiers = DecoratorUtil.spliceDecorators(node, decl, [
|
|
130
|
-
state.createDecorator(
|
|
105
|
+
state.createDecorator(INJECTABLE_IMPORT, 'Inject', ...this.processDeclaration(state, node)),
|
|
131
106
|
], 0);
|
|
132
107
|
|
|
133
108
|
// Doing decls
|
|
@@ -164,14 +139,14 @@ export class InjectableTransformer {
|
|
|
164
139
|
dependencies,
|
|
165
140
|
src: parent.name,
|
|
166
141
|
};
|
|
167
|
-
let
|
|
168
|
-
if (
|
|
169
|
-
|
|
142
|
+
let returnType = state.resolveReturnType(node);
|
|
143
|
+
if (returnType.key === 'literal' && returnType.ctor === Promise && returnType.typeArguments) {
|
|
144
|
+
returnType = returnType.typeArguments![0];
|
|
170
145
|
}
|
|
171
|
-
if (
|
|
172
|
-
config.target = state.getOrImport(
|
|
173
|
-
} else if (
|
|
174
|
-
config.target =
|
|
146
|
+
if (returnType.key === 'managed') {
|
|
147
|
+
config.target = state.getOrImport(returnType);
|
|
148
|
+
} else if (returnType.key === 'foreign') {
|
|
149
|
+
config.target = state.getForeignTarget(returnType);
|
|
175
150
|
}
|
|
176
151
|
|
|
177
152
|
// Build decl
|
|
@@ -183,7 +158,7 @@ export class InjectableTransformer {
|
|
|
183
158
|
return state.factory.updateMethodDeclaration(
|
|
184
159
|
node,
|
|
185
160
|
DecoratorUtil.spliceDecorators(node, dec, [
|
|
186
|
-
state.createDecorator(
|
|
161
|
+
state.createDecorator(INJECTABLE_IMPORT, 'InjectableFactory', ...args)
|
|
187
162
|
]),
|
|
188
163
|
node.asteriskToken,
|
|
189
164
|
node.name,
|
package/src/global.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import './types';
|
|
2
|
-
|
|
3
|
-
declare global {
|
|
4
|
-
/**
|
|
5
|
-
* @concrete node:buffer#Blob
|
|
6
|
-
*/
|
|
7
|
-
interface Blob { }
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @concrete node:buffer#File
|
|
11
|
-
*/
|
|
12
|
-
interface File { }
|
|
13
|
-
|
|
14
|
-
namespace NodeJS {
|
|
15
|
-
/**
|
|
16
|
-
* @concrete node:stream#Readable
|
|
17
|
-
*/
|
|
18
|
-
interface ReadableStream { }
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
declare module 'stream' {
|
|
23
|
-
/**
|
|
24
|
-
* @concrete node:stream#Readable
|
|
25
|
-
*/
|
|
26
|
-
interface Readable { }
|
|
27
|
-
}
|
package/src/internal/types.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export class AutoCreateTarget { }
|