Skip to content

Commit e7f9f90

Browse files
feat: global providers
1 parent 62f66c3 commit e7f9f90

24 files changed

+529
-112
lines changed

README.md

+30-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
- [Providers](#providers)
2424
- [Exports](#exports)
2525
- [Get the Container of a Module](#get-the-container-of-a-module)
26-
- [Code Coverage](#code-coverage)
26+
- [Testing](#testing)
2727
- [Support the Project](#support-the-project)
2828
- [License](#license)
2929

@@ -230,11 +230,37 @@ Below you will find a detailed explanation of each of the concepts that this lib
230230

231231
#### Get the Container of a Module
232232

233-
// TODO
233+
Ideally we shouldn't be accessing module containers directly to get a service. In either case, the `getModuleContainer` function allows you to get the container of a module in case you need to access it in an statement.
234+
235+
```typescript
236+
import { getModuleContainer, module, InversifySugar } from "inversify-sugar";
237+
import { injectable } from "inversify";
238+
239+
@injectable()
240+
class TestService {}
241+
242+
@module({
243+
providers: [TestService],
244+
})
245+
class TestModule {}
246+
247+
@module({
248+
imports: [TestModule],
249+
})
250+
class AppModule {}
251+
252+
InversifySugar.run(AppModule);
253+
254+
// Accessing the container of a imported module
255+
const testModuleContainer = getModuleContainer(TestModule);
256+
const testService = testModuleContainer.get(TestService);
257+
```
258+
259+
## Testing
234260

235-
## Code Coverage
261+
The complexity of the memory state during the execution of Inversify Sugar, managing multiple Inversify containers under the hood, is too high to ensure that it is working correctly without writing unit tests of each of the functionalities separately.
236262

237-
The complexity of the memory state during the execution of Inversify Sugar, managing multiple Inversify containers under the hood, is too high to be able to ensure that it is working correctly without writing unit tests of each of the functionalities separately.
263+
For this reason, a set of tests have been written that you can consult [here](./tests).
238264

239265
So you can use it without worries. You are facing a completely armored dependency system.
240266

+1-1
Loading
+1-1
Loading
+1-1
Loading
+1-1
Loading

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
"lint": "eslint . --ext .ts",
3535
"test:unit": "jest --coverage && jest-coverage-badges --input ./coverage/coverage-summary.json --output ./assets/badges/coverage",
3636
"test": "yarn test:unit",
37-
"build": "ttsc",
38-
"build:dev": "yarn build --watch",
37+
"build": "ttsc --project tsconfig.build.json",
38+
"build:dev": "yarn build --project tsconfig.build.json --watch",
3939
"yalc:publish": "npx yalc publish",
4040
"yalc:update": "npx yalc push --scripts --update --replace",
4141
"npm:publish": "yarn build && npm publish"

src/decorators/module.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
12
import { Container } from "inversify";
23
import { Constructor } from "../types";
34
import ModuleMetadata, { ModuleMetadataArgs } from "../types/ModuleMetadata";
@@ -8,14 +9,22 @@ export default function module({
89
exports = [],
910
}: ModuleMetadataArgs) {
1011
return (target: Constructor) => {
11-
const metadata = {
12+
const scopedProviders = providers.filter(
13+
(provider: any) => !provider.isGlobal
14+
);
15+
const globalProviders = providers.filter(
16+
(provider: any) => !!provider.isGlobal
17+
);
18+
const metadata: ModuleMetadata = {
1219
isModule: true,
1320
isBinded: false,
1421
container: new Container(),
22+
exportsContainer: new Container(),
1523
imports,
16-
providers,
24+
providers: scopedProviders,
25+
globalProviders,
1726
exports,
18-
} as ModuleMetadata;
27+
};
1928

2029
for (const key in metadata) {
2130
if (metadata.hasOwnProperty(key)) {

src/types/ModuleMetadata.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { keys } from "ts-transformer-keys";
22
import Provider from "./Provider";
33
import { Constructor } from ".";
4-
import { Container, interfaces } from "inversify";
4+
import { Container } from "inversify";
55

66
/**
77
* @description Interface defining the property object that describes the module.
@@ -24,18 +24,24 @@ export interface ModuleMetadataArgs {
2424
/**
2525
* @description Optional list of providers exported from this module.
2626
*/
27-
exports?: interfaces.ServiceIdentifier[];
27+
exports?: Provider[];
2828
}
2929

3030
export default interface ModuleMetadata {
3131
isModule: true;
3232
isBinded: boolean;
3333
container: Container;
34+
exportsContainer: Container;
3435
imports: Constructor[];
3536
providers: Provider[];
36-
exports: Constructor[];
37+
globalProviders: Provider[];
38+
exports: Provider[];
3739
}
3840

39-
export const IsModuleKey: keyof ModuleMetadata = "isModule";
41+
export const isModuleKey: keyof ModuleMetadata = "isModule";
42+
43+
export const isBindedKey: keyof ModuleMetadata = "isBinded";
44+
45+
export const containerKey: keyof ModuleMetadata = "container";
4046

4147
export const moduleMetadataKeys = keys<ModuleMetadata>();

src/types/Provider.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ interface WithProvide {
66
/**
77
* @description ServiceIdentifier / InjectionToken
88
*/
9-
provide: interfaces.ServiceIdentifier;
9+
provide: string | symbol;
10+
}
11+
12+
interface WithIsGlobal {
13+
/**
14+
* @description Flag that indicates if the provider is binded to the global container.
15+
*/
16+
isGlobal?: boolean;
1017
}
1118

1219
/**
@@ -17,14 +24,16 @@ export type ConstructorProvider<T = any> = Constructor<T>;
1724
/**
1825
* @description Interface defining a *Class* type provider.
1926
*/
20-
export interface ClassProvider<T = any> extends Partial<WithProvide> {
27+
export interface ClassProvider<T = any>
28+
extends Partial<WithProvide>,
29+
WithIsGlobal {
2130
/**
2231
* @description Instance of a provider to be injected.
2332
*/
2433
useClass: T;
2534
useValue?: never;
2635
/**
27-
* Binding scope of a provider.
36+
* @description Binding scope of a provider.
2837
* @default 'Singleton'
2938
*/
3039
scope?: interfaces.BindingScope;
@@ -33,7 +42,7 @@ export interface ClassProvider<T = any> extends Partial<WithProvide> {
3342
/**
3443
* @description Interface defining a *Value* type provider.
3544
*/
36-
export interface ValueProvider<T = any> extends WithProvide {
45+
export interface ValueProvider<T = any> extends WithProvide, WithIsGlobal {
3746
/**
3847
* @description Instance of a provider to be injected.
3948
*/
@@ -44,7 +53,7 @@ export interface ValueProvider<T = any> extends WithProvide {
4453
/**
4554
* @description Interface defining a *Factory* type provider. The scope of a factory provider is always singleton.
4655
*/
47-
export interface FactoryProvider<T = any> extends WithProvide {
56+
export interface FactoryProvider<T = any> extends WithProvide, WithIsGlobal {
4857
/**
4958
* @description Factory function to be injected.
5059
*/

src/utils/InversifySugar.ts

+17-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Container, interfaces } from "inversify";
22
import { Constructor } from "../types";
33
import importModule from "./importModule";
44
import ModuleMetadata from "../types/ModuleMetadata";
5-
import clc from "cli-color";
5+
import messagesMap from "./messagesMap";
66

77
/**
88
* @description InversifySugar is a utility class that helps you to bootstrap inversify and configure it.
@@ -18,7 +18,9 @@ export default class InversifySugar {
1818
*/
1919
public static debug = false;
2020

21-
private static _rootContainer: Container | undefined;
21+
private static isRunning = false;
22+
23+
private static _globalContainer = new Container();
2224

2325
private static _onModuleImported:
2426
| ((
@@ -28,11 +30,21 @@ export default class InversifySugar {
2830
) => void)
2931
| undefined;
3032

33+
static get globalContainer() {
34+
return InversifySugar._globalContainer;
35+
}
36+
3137
/**
3238
* @description This method is used to bootstrap inversify and import the AppModule.
3339
*/
34-
static run(AppModule: Constructor): Container {
35-
return importModule(AppModule, true);
40+
static run(AppModule: Constructor) {
41+
if (InversifySugar.isRunning) {
42+
throw new Error(messagesMap.alreadyRunning);
43+
}
44+
45+
InversifySugar.isRunning = true;
46+
47+
importModule(AppModule, true);
3648
}
3749

3850
static onModuleImported(
@@ -43,7 +55,7 @@ export default class InversifySugar {
4355
InversifySugar._onModuleImported?.(container, metadata, Module);
4456

4557
if (InversifySugar.debug) {
46-
console.log(clc.bold("[@module]"), clc.green(`${Module.name} imported.`));
58+
console.log(messagesMap.moduleImported(Module.name));
4759
}
4860
}
4961

@@ -56,18 +68,4 @@ export default class InversifySugar {
5668
) {
5769
InversifySugar._onModuleImported = value;
5870
}
59-
60-
static get rootContainer() {
61-
if (!InversifySugar._rootContainer) {
62-
console.warn(
63-
clc.xterm(250)("You are accessing the root container before it is set.")
64-
);
65-
}
66-
67-
return InversifySugar._rootContainer;
68-
}
69-
70-
static setRootContainer(container: Container | undefined) {
71-
InversifySugar._rootContainer = container;
72-
}
7371
}

src/utils/bindProvider.ts

+2-19
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import { Container, interfaces } from "inversify";
2+
import { Container } from "inversify";
33
import Provider, {
44
ClassProvider,
55
ConstructorProvider,
66
FactoryProvider,
77
ValueProvider,
88
} from "../types/Provider";
9-
import InversifySugar from "./InversifySugar";
109
import isConstructorProvider from "./validation/isConstructorProvider";
1110
import isClassProvider from "./validation/isClassProvider";
1211
import isValueProvider from "./validation/isValueProvider";
1312
import isFactoryProvider from "./validation/isFactoryProvider";
13+
import setScope from "./setScope";
1414

1515
const bindProvider = (provider: Provider, container: Container) => {
1616
if (isConstructorProvider(provider)) {
@@ -38,21 +38,4 @@ const bindProvider = (provider: Provider, container: Container) => {
3838
}
3939
};
4040

41-
function setScope(
42-
binding: interfaces.BindingInWhenOnSyntax<any>,
43-
scope: interfaces.BindingScope = InversifySugar.defaultScope
44-
) {
45-
switch (scope) {
46-
case "Transient":
47-
binding.inTransientScope();
48-
break;
49-
case "Request":
50-
binding.inRequestScope();
51-
break;
52-
case "Singleton":
53-
binding.inSingletonScope();
54-
break;
55-
}
56-
}
57-
5841
export default bindProvider;

src/utils/getModuleContainer.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Constructor } from "../types";
2+
import ModuleMetadata, { moduleMetadataKeys } from "../types/ModuleMetadata";
3+
import getAllMetadata from "./getAllMetadata";
4+
5+
export default function getModuleContainer(Module: Constructor) {
6+
const metadata = getAllMetadata<ModuleMetadata>(
7+
Module.prototype,
8+
moduleMetadataKeys
9+
);
10+
11+
return metadata.container;
12+
}

0 commit comments

Comments
 (0)