ioc-service-container
Advanced tools
Comparing version 1.6.1 to 2.0.0
@@ -27,4 +27,4 @@ "use strict"; | ||
get, | ||
set | ||
set, | ||
}); | ||
} |
import ServiceContainer from './ServiceContainer'; | ||
import { inject } from './decorators'; | ||
import { scg } from './helpers'; | ||
export { ServiceContainer, inject, scg, }; | ||
export { ServiceContainer, inject, scg }; |
declare const _default: { | ||
/** | ||
* | ||
* @param id to identify the service in the container, it can be mapped to a property name via the inject decorator | ||
* @param factoryOrClassReference to create the instance of the service | ||
* @param buildInstantly by default the service is only instantiated on demand, if required you are able to build the service directly | ||
* Register a service in the container | ||
* @param id {string} to identify the service in the container, it can be mapped to a property name via the inject decorator | ||
* @param provider factory, constructable or entity which is stored in the container | ||
*/ | ||
set(id: string, factoryOrClassReference: Factory | Function, buildInstantly?: boolean): void; | ||
get<T>(id: string): T; | ||
override(id: string, factoryOrClassReference: Factory | Function, buildInstantly?: boolean): void; | ||
set<T>(id: string, provider: NoUndefined<T>): void; | ||
/** | ||
* Overrides a service in the container. If the service does not exist, it will be registered. | ||
* @param id {string} | ||
* @param provider | ||
*/ | ||
override<T_1>(id: string, provider: NoUndefined<T_1>): void; | ||
/** | ||
* Get a service from the container. If the service is not instantiated, it will be instantiated. | ||
* @param id {string} | ||
*/ | ||
get<T_2>(id: string): T_2; | ||
/** | ||
* Check if the service is set in the container | ||
* @param id {string} | ||
*/ | ||
isSet(id: string): boolean; | ||
/** | ||
* Reset the container | ||
*/ | ||
reset(): void; | ||
}; | ||
export default _default; | ||
type Factory = () => any; | ||
type NoUndefined<T> = T extends undefined ? never : T; |
@@ -6,14 +6,33 @@ "use strict"; | ||
/** | ||
* | ||
* @param id to identify the service in the container, it can be mapped to a property name via the inject decorator | ||
* @param factoryOrClassReference to create the instance of the service | ||
* @param buildInstantly by default the service is only instantiated on demand, if required you are able to build the service directly | ||
* Register a service in the container | ||
* @param id {string} to identify the service in the container, it can be mapped to a property name via the inject decorator | ||
* @param provider factory, constructable or entity which is stored in the container | ||
*/ | ||
set(id, factoryOrClassReference, buildInstantly = false) { | ||
const lowerId = id.toLowerCase(); | ||
if (this.isSet(lowerId)) { | ||
set(id, provider) { | ||
if (this.isSet(id)) { | ||
throw new Error(`Service [${id}] is already registered`); | ||
} | ||
this.override(lowerId, factoryOrClassReference, buildInstantly); | ||
this.override(id, provider); | ||
}, | ||
/** | ||
* Overrides a service in the container. If the service does not exist, it will be registered. | ||
* @param id {string} | ||
* @param provider | ||
*/ | ||
override(id, provider) { | ||
const lowerId = id.toLowerCase(); | ||
if (isConstructable(provider)) { | ||
services[lowerId] = { factory: () => new provider() }; | ||
return; | ||
} | ||
if (typeof provider === 'function') { | ||
services[lowerId] = { factory: provider }; | ||
return; | ||
} | ||
services[lowerId] = { instance: provider }; | ||
}, | ||
/** | ||
* Get a service from the container. If the service is not instantiated, it will be instantiated. | ||
* @param id {string} | ||
*/ | ||
get(id) { | ||
@@ -25,30 +44,30 @@ var _a; | ||
} | ||
if (!service.instance) { | ||
if (service.instance === undefined) { | ||
service.instance = (_a = service.factory) === null || _a === void 0 ? void 0 : _a.call(service); | ||
service.factory = undefined; | ||
delete service.factory; | ||
} | ||
return service.instance; | ||
}, | ||
override(id, factoryOrClassReference, buildInstantly = false) { | ||
const lowerId = id.toLowerCase(); | ||
let factory = getFactory(factoryOrClassReference); | ||
services[lowerId] = { | ||
factory: buildInstantly ? undefined : factory, | ||
instance: buildInstantly ? factory() : undefined, | ||
}; | ||
}, | ||
/** | ||
* Check if the service is set in the container | ||
* @param id {string} | ||
*/ | ||
isSet(id) { | ||
return services[id.toLowerCase()] !== undefined; | ||
}, | ||
/** | ||
* Reset the container | ||
*/ | ||
reset() { | ||
services = {}; | ||
} | ||
}, | ||
}; | ||
function isConstructable(obj) { | ||
return !!obj.prototype && !!obj.prototype.constructor.name; | ||
/** | ||
* Check if the object is a class | ||
* @param value | ||
* @returns {boolean} true if the object is a class | ||
* @see https://stackoverflow.com/a/46320004 | ||
*/ | ||
function isConstructable(value) { | ||
return typeof value === 'function' && !!value.prototype && !!value.prototype.constructor.name; | ||
} | ||
function getFactory(factoryOrClassReference) { | ||
return isConstructable(factoryOrClassReference) | ||
? () => new factoryOrClassReference() | ||
: factoryOrClassReference; | ||
} |
{ | ||
"name": "ioc-service-container", | ||
"version": "1.6.1", | ||
"version": "2.0.0", | ||
"description": "Lightweight ioc service container", | ||
@@ -11,3 +11,4 @@ "main": "dist/index.js", | ||
"test": "jest", | ||
"sonar": "sonar-scanner -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=mrcwbr -Dsonar.projectKey=ioc-service-container -Dsonar.sources=src -Dsonar.tests=tests" | ||
"format": "prettier --write .", | ||
"format:check": "prettier . --check" | ||
}, | ||
@@ -17,7 +18,7 @@ "author": "mrcwbr", | ||
"devDependencies": { | ||
"@types/jest": "^29.5.3", | ||
"jest": "^29.6.2", | ||
"sonarqube-scanner": "^2.9.1", | ||
"ts-jest": "^29.1.1", | ||
"ts-node": "^10.9.1", | ||
"@types/jest": "^29.5.12", | ||
"jest": "^29.7.0", | ||
"prettier": "3.3.3", | ||
"ts-jest": "^29.2.4", | ||
"ts-node": "^10.9.2", | ||
"typescript": "^4.9.5" | ||
@@ -27,4 +28,3 @@ }, | ||
"dist/*", | ||
"README.md", | ||
"Changelog.md" | ||
"README.md" | ||
], | ||
@@ -31,0 +31,0 @@ "repository": { |
@@ -1,3 +0,5 @@ | ||
# ioc-service-container | ||
# IoC Service Container | ||
> This is a lightweight **zero-dependency** library for a service container written in [TypeScript](https://www.typescriptlang.org). | ||
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ioc-service-container&metric=alert_status)](https://sonarcloud.io/dashboard?id=ioc-service-container) | ||
@@ -8,2 +10,3 @@ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=ioc-service-container&metric=bugs)](https://sonarcloud.io/dashboard?id=ioc-service-container) | ||
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ioc-service-container&metric=security_rating)](https://sonarcloud.io/dashboard?id=ioc-service-container) | ||
![min-size](https://badgen.net/bundlephobia/min/ioc-service-container) | ||
@@ -15,25 +18,17 @@ ![min-size-g-zip](https://badgen.net/bundlephobia/minzip/ioc-service-container) | ||
<a href="https://www.buymeacoffee.com/Mrcwbr" target="_blank"> | ||
<img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" | ||
alt="Buy Me A Coffee" | ||
style="height: 41px !important;width: 174px !important;box-shadow: 0 3px 2px 0 rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0 3px 2px 0 rgba(190, 190, 190, 0.5) !important;" > | ||
</a> | ||
> This is a lightweight **zero-dependency** library for a service container written in TypeScript. | ||
## Features | ||
* **Fully typed** | ||
* **100% TypeScript written** | ||
* **100% test coverage** | ||
* **0 dependencies** | ||
* **< 2 KB package size** | ||
* **Typescript Decorator support** | ||
* **Simple API** | ||
* **Works beautiful with [jest-mock-extended](https://www.npmjs.com/package/jest-mock-extended)** | ||
- **Fully typed** | ||
- **100 % [TypeScript](https://www.typescriptlang.org)** | ||
- **100 % Test coverage** | ||
- **0 Dependencies** | ||
- **< 2 KB Package size** | ||
- **[Typescript Decorator](https://www.typescriptlang.org/docs/handbook/decorators.html) support** | ||
- **Simple API** | ||
- **Works beautiful with [jest-mock-extended](https://www.npmjs.com/package/jest-mock-extended)** | ||
## Demo | ||
In this [StackBlitz-Demo](https://stackblitz.com/edit/react-ts-qya4xy?file=App.tsx) you can see a demonstration of | ||
the `ioc-service-container`. In the `App.tsx` you can verify that the `UserService` is fully typed without importing the | ||
In this [StackBlitz-Demo](https://stackblitz.com/edit/react-ts-qya4xy?file=App.tsx) you can see a demonstration of the | ||
`ioc-service-container`. In the `App.tsx` you can verify that the `UserService` is fully typed without importing the | ||
class. | ||
@@ -56,11 +51,12 @@ | ||
// Import your services | ||
import { TestApi } from '../your-path/to/TestApi' | ||
import { FooApi } from '../your-path/to/FooApi' | ||
import { TestService } from '../your-path/to/TestService' | ||
import { TestApi } from '../your-path/to/TestApi'; | ||
import { FooApi } from '../your-path/to/FooApi'; | ||
import { TestService } from '../your-path/to/TestService'; | ||
// Create the mapping between ServiceId and Service | ||
type IoCTypes = { | ||
TestApi: TestApi, | ||
FooApi: FooApi, | ||
TestService: TestService, | ||
TestApi: TestApi; | ||
FooApi: FooApi; | ||
TestService: TestService; | ||
myString: string; | ||
// ... | ||
@@ -77,4 +73,4 @@ }; | ||
According to this you have to pass a factory or a class reference of your required services to the ioc container. So | ||
at the initial script of your application you call a function named e.g. `setupService`: | ||
According to this you have to pass a factory, a constructable or an entity to the ioc container. So at | ||
the initial script of your application you call a function named e.g. `setupService`: | ||
@@ -87,8 +83,10 @@ ```typescript | ||
ServiceContainer.set('FooApi', () => new CustomFooApi()); // setup by custom factory | ||
ServiceContainer.set('TestService', TestService, true); // instantieate immediately | ||
const testService = new TestService(); | ||
ServiceContainer.set('TestService', testService); // pass the instance directly | ||
ServiceContainer.set('myString', 'hello world'); // pass primitive values | ||
} | ||
``` | ||
The factory is only instantiated at need. You can pass the `buildInstantly` attribute if the service should be | ||
initialized immediately e.g. for setting up [Sentry](https://sentry.io/welcome/) in a `LoggingService`. | ||
The factory is only instantiated at need. | ||
@@ -107,4 +105,4 @@ ### 3. Inject services | ||
> This requires `"experimentalDecorators": true` to be enabled in your `tsconfig.json` | ||
> (See [Typescript Docs](https://www.typescriptlang.org/tsconfig#experimentalDecorators)) | ||
> This requires `"experimentalDecorators": true` to be enabled in your `tsconfig.json` (See | ||
> [Typescript Docs](https://www.typescriptlang.org/tsconfig#experimentalDecorators)) | ||
@@ -114,3 +112,3 @@ ```typescript | ||
@inject | ||
private readonly customApi!: Api; // Important is the naming of the property, it's mapped to the service id | ||
private readonly customApi!: Api; // Important is the name of the property, it's mapped to the service id | ||
@@ -120,12 +118,12 @@ @inject('FooApi') // If you don't want to name your property like the service id, pass the id as parameter | ||
private readonly fooApi = ServiceContainer.get<Api>('FooApi') // Use this syntax if you don't want to use decorators | ||
private readonly fooApi = ServiceContainer.get<Api>('FooApi'); // Use this syntax if you don't want to use decorators | ||
private readonly barApi = scg('BarApi') // Shortcut for ServiceContainer.get() | ||
private readonly barApi = scg('BarApi'); // Shortcut for ServiceContainer.get() | ||
} | ||
``` | ||
``` | ||
### 4. Other Use-Cases | ||
For Testing or similar use cases you have the option to | ||
use `ServiceContainer.isSet('anId')`, `ServiceContainer.override('anId', () => 123)` or `ServiceContainer.reset()`. | ||
For Testing or similar use cases you have the option to use `ServiceContainer.isSet('anId')`, | ||
`ServiceContainer.override('anId', 123)` or `ServiceContainer.reset()`. | ||
@@ -148,2 +146,2 @@ ## Background | ||
The goal of DI is to encapsulate the dependencies of a class. The CustomService should work without knowing which api it | ||
is using. The following structure should be created. | ||
is using. |
167
13508
11
139