Socket
Socket
Sign inDemoInstall

dependency-injection-cat

Package Overview
Dependencies
34
Maintainers
1
Versions
67
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.11 to 0.0.12-beta

CHANGELOG.md

59

index.d.ts

@@ -1,4 +0,57 @@

export * from './decorators';
export declare class container {
static get<T>(beanName?: string): T;
export interface IDIContext<TBeans> {
getBeans(): TBeans;
getBean<TBean extends keyof TBeans>(beanName: TBean): TBeans[TBean];
}
export interface IInitContextProps {
name: string;
key?: any;
}
export interface IInitContextPropsWithConfig<TConfig> extends IInitContextProps {
config: TConfig;
}
export declare type TBeanName = string;
declare class Container {
/**
* TBeans should be a plain interface without extending
*/
initContext<TBeans>(props: IInitContextProps): IDIContext<TBeans>;
initContext<TBeans, TConfig>(props: IInitContextPropsWithConfig<TConfig>): IDIContext<TBeans>;
/**
* TBeans should be a plain interface without extending
*/
getOrInitContext<TBeans>(props: IInitContextProps): IDIContext<TBeans>;
getOrInitContext<TBeans, TConfig>(props: IInitContextPropsWithConfig<TConfig>): IDIContext<TBeans>;
/**
* TBeans should be a plain interface without extending
*/
getContext<TBeans extends Record<TBeanName, any>>(props: IInitContextProps): IDIContext<TBeans>;
clearContext(props: IInitContextProps): void;
private static throwInitializationError;
}
export declare const container: Container;
/**
* TBeans should be a plain interface without extending
*/
export declare abstract class CatContext<TBeans, TConfig = null> {
protected constructor();
get config(): TConfig;
}
export declare abstract class GlobalCatContext {
protected constructor();
}
export declare type TClassConstructor<T> = new (...args: any[]) => T;
export declare type TBeanScope = "prototype" | "singleton";
export interface IBeanConfig {
scope?: TBeanScope;
}
export declare type ConfigurableMethodBean = (beanConfig: IBeanConfig) => PropertyDecorator;
export declare type PropertyBean = <T>(clazz: TClassConstructor<T>, beanConfig?: IBeanConfig) => T;
export declare type TBean = PropertyDecorator & ConfigurableMethodBean & PropertyBean;
export declare const Bean: TBean;
export declare const Qualifier: <T extends string>(beanName: T extends "" ? never : T) => ParameterDecorator;
export declare type TPostConstruct = PropertyDecorator & MethodDecorator;
export declare const PostConstruct: TPostConstruct;
export declare type TBeforeDestruct = PropertyDecorator & MethodDecorator;
export declare const BeforeDestruct: TBeforeDestruct;
export {};

29

index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.container = void 0;
var tslib_1 = require("tslib");
var libraryName_1 = require("./constants/libraryName");
tslib_1.__exportStar(require("./decorators"), exports);
var container = /** @class */ (function () {
function container() {
}
container.get = function (beanName) {
//TODO add link to configure guide
throw new Error("It seems, like " + libraryName_1.libraryName + " was not configured correctly");
};
return container;
}());
exports.container = container;
__exportStar(require("./external/containter"), exports);
__exportStar(require("./external/IDIContext"), exports);
__exportStar(require("./external/CatContext"), exports);
__exportStar(require("./external/GlobalCatContext"), exports);
__exportStar(require("./external/decorators"), exports);
{
"name": "dependency-injection-cat",
"version": "0.0.11",
"version": "0.0.12-beta",
"main": "index.js",

@@ -9,2 +9,15 @@ "types": "index.d.ts",

"private": false,
"keywords": [
"ioc",
"inversion of control",
"di",
"dependency injection",
"typescript",
"babel",
"webpack",
"cat",
"clean architecture",
"container",
"dependency injection container"
],
"repository": {

@@ -14,47 +27,16 @@ "url": "https://github.com/artem1458/dependency-injection-cat"

"peerDependencies": {
"typescript": "^3.8.2"
"typescript": "4.X.X"
},
"devDependencies": {
"@types/chokidar": "^2.1.3",
"@types/glob": "^7.1.2",
"@types/jest": "^26.0.3",
"@types/jest-sinon": "^1.0.1",
"@types/lodash": "^4.14.155",
"@types/node": "^14.0.27",
"@types/sinon": "^9.0.4",
"@types/typescript": "^2.0.0",
"@types/webpack": "^4.41.21",
"@typescript-eslint/eslint-plugin": "^4.3.0",
"@typescript-eslint/parser": "^4.3.0",
"awesome-typescript-loader": "^5.2.1",
"clean-webpack-plugin": "^3.0.0",
"dependency-injection-cat": "./dist/src",
"eslint": "^7.10.0",
"html-webpack-plugin": "^4.3.0",
"jest": "^26.1.0",
"jest-sinon": "^1.0.4",
"json-beautify": "^1.1.1",
"less-loader": "^6.1.1",
"memfs": "^3.2.0",
"sinon": "^9.0.2",
"style-loader": "^1.2.1",
"ts-auto-mock": "^2.3.3",
"ts-jest": "^26.1.1",
"ts-loader": "^7.0.5",
"ts-mockito": "^2.6.1",
"ts-node": "^8.10.2",
"tsconfig-paths-webpack-plugin": "^3.3.0",
"ttypescript": "^1.5.10",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-watch-files-plugin": "^1.0.3"
},
"dependencies": {
"chalk": "^4.1.0",
"chokidar": "^3.4.2",
"glob": "^7.1.6",
"lodash": "^4.17.20",
"tsconfig-paths": "^3.9.0"
"graphlib": "^2.1.8",
"json-beautify": "^1.1.1",
"line-column": "^1.0.2",
"lodash": "^4.17.21",
"md5": "^2.3.0",
"minimatch": "^3.0.4",
"tsconfig-paths": "^3.9.0",
"upath": "^2.0.1"
}
}
}

@@ -1,31 +0,48 @@

![npm](https://img.shields.io/npm/v/dependency-injection-cat?style=flat)
# dependency-injection-cat
# Dependency Injection Cat
Dependency Injection Cat is a TypeScript-only library which allows you to implement
the Dependency Inversion pattern with Dependency Injection
<!-- toc -->
- [Installation](#installation)
* [Config options](#config-options)
* [Configuration with Webpack](#configuration-with-webpack)
* [Configuration with ttypescript](#configuration-with-ttypescript)
- [Usage](#usage)
- [Bean](#bean)
* [Rules](#bean-rules)
* [Syntax](#bean-syntax)
* [Configuration object](#bean-configuration-object)
- [Qualifier](#qualifier)
* [What is it?](#what-is-it)
* [Rules](#qualifier-rules)
* [Syntax](#qualifier-syntax)
- [Container](#container)
* [Rules](#container-rules)
* [Syntax](#container-syntax)
<!-- tocstop -->
DI Cat is a truly clean DI-container, which allows you not to pollute your business logic with decorators from DI/IOC libraries!
-----
[![npm](https://img.shields.io/npm/v/dependency-injection-cat?style=flat)](https://www.npmjs.com/package/dependency-injection-cat)
-----
## Example
```tsx
//ApplicationContext.di.ts
class ApplicationContext extends CatContext<IBeans> {
useCase = Bean<IUseCase>(UseCase)
mobxRepository = Bean<IRepository>(MobxRepository)
}
//UseCase.ts
export class UseCase implements IUseCase {
constructor(
private repository: IRepository,
) {}
makeBusinessLogic() {}
}
//Your application.tsx
export const UIComponent: React.FC = () => {
const appContext = container.getContext<IBeans>({name: 'ApplicationContext'});
const {useCase} = appContext.getBeans();
return (
<button onClick={useCase.makeBusinessLogic}> Click me! </button>
)
}
```
## Installation
**Yarn**
#### Yarn
```bash
yarn add dependency-injection-cat
```
**NPM**
#### NPM
```bash

@@ -35,66 +52,141 @@ npm install dependency-injection-cat

#### Config options
```ts
interface TransformerConfig {
diConfigPattern?: string; // Glob pattern, default value = '**/*.diconfig.ts'
ignorePatterns?: string[]; // Array of Glob patterns, default value = ['**/node_modules/**']
#### Requirements for TSconfig
```json5
//tsconfig.json
{
"compilerOptions": {
"baseUrl": "Base url should be specified!"
}
}
```
#### Configuration with Webpack
Dependency Injection Cat supports transpileOnly mode for faster builds! [More Info](https://github.com/TypeStrong/ts-loader#transpileonly)
<br/>
With Webpack, You can use any TypeScript-related loader that supports custom transformers, e.g. awesome-typescript-loader or ts-loader
#### Webpack + Babel
<h5>webpack.config.js</h5>
```typescript
//webpack.config.js
const DiCatWebpackPlugin = require('dependency-injection-cat/plugins/webpack').default;
const DiCatBabelTransformer = require('dependency-injection-cat/transformers/babel');
```js
const dependencyInjectionCatTransformer = require('dependency-injection-cat/transformer').default;
module.exports = {
// ...
//...
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader', // or 'awesome-typescript-loader'
test: /\.(t|j)sx?$/,
loader: 'babel-loader',
options: {
transpileOnly: true, // If set transpileOnly: true, you're loosing TypeChecking
//If you're using ttypescript
compiler: 'ttypescript',
//If you don't use ttypescript, you should pass transformer
getCustomTransformers: program => ({
before: [
dependencyInjectionCatTransformer(program),
],
}),
},
},
],
//Should be false, because babel caching files
//and don't call di-cat babel transformer, if your not *.di.* files was changed
cacheDirectory: false,
plugins: [
[
DiCatBabelTransformer,
{
//Here is configuration options, see below
}
]
]
}
}
]
},
};
plugins: [
//Without this plugin, a compilation with DI errors will be successful
new DiCatWebpackPlugin(),
]
}
```
<h5>With custom options</h5>
In webpack.config.js you can pass a second parameter to the transformer:
```json5
//tsconfig.json
{
"compilerOptions": {
//Should be specified
"baseUrl": "your base url"
}
}
```
```js
before: [
dependencyInjectionCatTransformer(program, {
diConfigPattern: '**/config/**/*.diconfig.ts'
})
]
#### Webpack + TS-Loader
```typescript
//webpack.config.js
const DiCatWebpackPlugin = require('dependency-injection-cat/plugins/webpack').default;
const diCatTransformer = require('dependency-injection-cat/transformers/typescript').default;
module.exports = {
//...
module: {
rules: [
{
test: /\.(t|j)sx?$/,
use: {
loader: 'ts-loader',
options: {
getCustomTransformers: (program) => ({
before: [diCatTransformer(program, {
//Here is configuration options, see below
})],
}),
}
}
}
]
},
plugins: [
//Without this plugin, a compilation with DI errors will be successful
new DiCatWebpackPlugin(),
]
}
```
#### Configuration with ttypescript
Check out ttypescript's [README](https://github.com/cevek/ttypescript/blob/master/README.md) for more information
```json5
//tsconfig.json
{
"compilerOptions": {
//Should be specified
"baseUrl": "your base url"
}
}
```
<h5>tsconfig.json</h5>
#### Webpack + TS-Loader + ttypescript
```json
```typescript
//webpack.config.js
const DiCatWebpackPlugin = require('dependency-injection-cat/plugins/webpack').default;
module.exports = {
//...
module: {
rules: [
{
test: /\.(t|j)sx?$/,
use: {
loader: 'ts-loader',
options: {
compiler: 'ttypescript'
}
}
}
]
},
plugins: [
//Without this plugin, a compilation with DI errors will be successful
new DiCatWebpackPlugin(),
]
}
```
```json5
//tsconfig.json
{
"compilerOptions": {
//Should be specified
"baseUrl": "your base url",
"plugins": [
{
"transform": "dependency-injection-cat/transformer"
"transform": "dependency-injection-cat/transformers/typescript",
//Here is configuration options, see below
}

@@ -106,11 +198,17 @@ ]

<h5>With custom options</h5>
#### TTypescript
```json
```json5
//tsconfig.json
{
"compilerOptions": {
//Should be specified
"baseUrl": "your base url",
"plugins": [
{
"transform": "dependency-injection-cat/transformer",
"diConfigPattern": "**/config/**/*.diconfig.ts"
"transform": "dependency-injection-cat/transformers/typescript",
//Here is configuration options, see below
},
{
"transform": "dependency-injection-cat/plugins/typescript/ReportErrorsTypescriptPlugin"
}

@@ -122,223 +220,522 @@ ]

## Usage
## Configuration options
```ts
// requesters.diconfig.ts
import { Bean, Qualifier } from 'dependency-injection-cat';
import { IRequester } from '../IRequester';
import { Requester } from '../Requester';
import { ILogger } from '../ILogger';
```typescript
{
diConfigPattern: string | undefined; // Glob pattern, default value. Default: '**/*.di.ts'
ignorePatterns: Array<string> | undefined; // Array of Glob patterns, default value. Default: ['**/node_modules/**']
disableLogoPrint: boolean | undefined; // Disable exposing dependency-injections-cat logo into console
}
```
export class Requesters {
@Bean
requester(logger: ILogger): IRequester {
return new Requester(logger);
}
//or
requester = Bean<IRequester>(Requester);
## Hot Reload
Currently, correct hot reload supported only with DI-Cat Webpack plugin, and for non-global CatContexts.
#### Known issues:
- When using webpack+babel - webpack or babel don't trigger rebuild of files where defined only types/interfaces,
...etc. Because of this issue, when adding new values to the TBean interface, context will not recompile, and missing
required beans will not be reported. To solve this issue, just make some changes in file where CatContext are defined,
it will trigger recompilation.
## CatContext
CatContext it's a place, where you should define your **Beans**
```typescript
class CatContext<TBeans, TConfig = null> {}
```
##### Rules
- Name of context should be unique
- Name of context "Global" is preserved for DI container, see **GlobalCatContext**
### TBeans
TBeans is an interface, of Beans that will be given out of context. Should be the same as in **container** access calls.
##### Rules
- Should be a plain interface without extending, should not have indexed signatures
- Should be placed in your source files, not from node_modules
- **Bean** name should not be **getBean** or **getBeans**, it's reserved names for di-container
```typescript
export interface IBeans {
useCase: IUseCase;
//Another beans...
}
```
### TConfig
If you need to pass additional parameters to your classes, for example ID, or something else, you should specify a type
for TConfig (default is null)
```typescript
export interface IConfig {
id: string;
}
// loggers.diconfig.ts
import { Bean } from 'dependency-injection-cat';
import { ILogger } from '../ILogger';
import { Logger } from '../Logger';
class ApplicationContext extends CatContext<IBeans, IConfig> {
@Bean
useCase(): IUseCase {
const {id} = this.config;
export class Loggers {
@Bean
logger(): ILogger {
return new Logger();
}
//or
@Bean
logger = Bean<ILogger>(Logger);
return new UseCase(id);
}
}
```
// main.ts
## GlobalCatContext
Sometimes you want to describe common dependencies that will be used all across your application, for these purposes you
should use GlobalCatContext.
#### Rules
- You can not get beans from the Global Context using container access
#### Syntax and usage
```typescript
//GlobalApplicationContext.di.ts
class GlobalApplicationContext extends GlobalCatContext {
@Bean
logger(): ILogger {
return new Logger();
}
}
//ApplicationContext.di.ts
class AppContext extends CatContext<IBeans> {
@Bean
useCase(
//If Bean for ILogger will be defined in current context, bean from current context will be injected
logger: ILogger
): IUseCase {
return new UseCase(logger)
}
}
```
## Container
Using container you can control your contexts. You can't access container inside files where declared contexts.
#### Rules
- Name of context in container access calls, should not be "Global", it's reserved for the **GlobalCatContext**
### TBeans
TBeans is an interface, of Beans that will be given out of context. Should be the same as in **CatContext**.
### initContext
InitContext creates instance of **context** by key (if specified). Also you can pass config to your context using
initContext.
```typescript
container.initContext<TBeans, TConfig>({
key? : any, //Can be any value, you can use it for creating pool of contextMap.
name: string, //It's the name of the class in which you specified the Beans. Should be a string literal.
config? : TConfig, //Config that will be transferred to the context
});
```
#### initContext usage
```typescript
//InitContext without config and keys
import { container } from 'dependency-injection-cat';
import { IRequester } from './IRequester';
import { IBeans } from './IBeans';
const requester = container.get<IRequester>();
const applicationContext = container.initContext<IBeans>({name: 'ApplicationContext'});
```
requester.makeRequest();
```typescript
//InitContext with config and keys
import { container } from 'dependency-injection-cat';
import { IBeans } from './IBeans';
import { IConfig } from './IConfig';
const applicationContextBySomeKey = container.initContext<IBeans, IConfig>({
name: 'ApplicationContext',
key: 'userId',
config: {userId: 'userId'},
});
```
### getContext
getContext returns instance of **context** by key (if specified). Can be used only after initialization of context, or
error will be thrown.
```typescript
container.getContext<TBeans>({
key? : any,
name: string, //It's the name of the class in which you specified the Beans. Should be a string literal.
});
```
#### getContext usage
```typescript
//GetContext without keys
import { container } from 'dependency-injection-cat';
const context = container.getContext<TBeans>({name: 'ApplicationContext'});
```
```typescript
//GetContext with keys
import { container } from 'dependency-injection-cat';
const context = container.getContext<TBeans>({
name: 'ApplicationContext',
key: 'userId',
});
```
### getOrInitContext
GetOrInitContext return instance of **context** by key (if it was previously initialized), if wasn't - will be
created and returned a new instance of context.
```typescript
container.getOrInitContext<TBeans, TConfig>({
key? : any, //Can be any value, you can use it for creating pool of contextMap.
name: string, //It's the name of the class in which you specified the Beans. Should be a string literal.
config? : TConfig, //Config that will be transferred to the context
});
```
#### getOrInitContext usage
```typescript
//Accessing or initialization context without config and keys
import { container } from 'dependency-injection-cat';
import { IBeans } from './IBeans';
const applicationContext = container.getOrInitContext<IBeans>({name: 'ApplicationContext'});
```
```typescript
//Accessing or initialization context with config and keys
import { container } from 'dependency-injection-cat';
import { IBeans } from './IBeans';
import { IConfig } from './IConfig';
const applicationContextBySomeKey = container.getOrInitContext<IBeans, IConfig>({
name: 'ApplicationContext',
key: 'userId',
config: {userId: 'userId'},
});
```
### clearContext
clearContext should be used to clear instances of **Beans**. Can be used, for example when un-mounting components.
```typescript
container.clearContext({
name: string, //It's the name of the class in which you specified the Beans. Should be a string literal.
key? : any,
})
```
#### clearContext usage
```typescript
//ClearContext without keys
import { container } from 'dependency-injection-cat';
container.clearContext({name: 'ApplicationContext'});
```
```typescript
//ClearContext without keys
import { container } from 'dependency-injection-cat';
container.clearContext({
name: 'ApplicationContext',
key: 'userId',
});
```
## Bean
<h4 id="bean-rules">Rules</h4>
A Bean is an object that is instantiated, assembled, and managed by IOC container (**Definition from Spring Framework documentation**)
Beans can have dependencies, and they also should be defined as a beans or passed manually.
<!-- toc -->
- Bean should be a class member (property or method)
- Beans should not have cyclic dependencies (it will throw compilation error)
- Beans should not have duplicate declarations
- Beans can be used only in **diconfig.ts** files (or files with another pattern declared in transformer config)
- Bean type should not be empty (it will throw compilation error)
- Bean type should not be primitive (it will throw compilation error)
- All bean dependencies should be typed, and type should not be primitive
<!-- tocstop -->
### Bean configuration
<h4 id="bean-syntax">Syntax</h4>
Beans support 2 kinds of syntax
```typescript
interface IBeanConfiguration {
scope?: 'prototype' | 'singleton';
/* Should be a string literal. Default value is 'singleton'. When singleton, will be created only 1 instance of the Bean, when 'prototype', for each getting of Bean will be created a new instance */
}
```
<h5>First</h5>
### Bean features
```ts
import { Bean } from 'dependency-injection-cat';
- Detect cyclic dependencies at compile time
- Detect missing **Beans** at compile time
class SuperClass {
//Without any dependencies
@Bean
someMethod(): Interface {
return new ImplementationOfInterface();
}
### Property Bean
//With Bean dependencies
@Bean
someMethod(
dependency1: InterfaceOfDependency1,
dependency2: InterfaceOfDependency2,
): Interface {
return new ImplementationOfInterface(dependency1, dependency2);
}
Property Beans resolving class dependencies automatically from the constructor of a class that passed to **Bean**.
//With Bean configuration
@Bean({ qualifier: 'someCoolImpl', scope: 'prototype' })
someMethod(
dependency1: InterfaceOfDependency1,
dependency2: InterfaceOfDependency2,
): Interface {
return new ImplementationOfInterface(dependency1, dependency2);
}
#### Rules
//Beans do not support arrow-functions methods, this will throw an error
@Bean
someMethod = (): Interface => new ImplementationOfInterface();
- Each class constructor parameter should be defined as a **Bean** in the **Context**
- Each class constructor parameter should have a type, type should not be primitive. To pass primitive values to the
constructor, check out **Method Beans**
//Beans should have complex types, this will throw an error
@Bean
someMethod(): number | string | any {
return new ImplementationOfInterface();
}
#### Syntax
//Should not have cyclyc dependencies, this will throw an error
@Bean
someMethod(
dependency1: InterfaceOfDependency1, //Cyclic dependency
): Interface {
return new ImplementationOfInterface(dependency1);
}
```typescript
class ApplicationContext extends CatContext<IBeans> {
//First argument passed in Bean should be a class, di-container will try to resolve class dependencies
useCase = Bean(UseCaseClass);
@Bean
someMethod2(
dependency1: Interface, //Cyclic dependency
): InterfaceOfDependency1 {
return new ImplementationOfDependency(dependency1);
}
//To specify type of Bean you can use Generic parameter, or set type of property
useCase = Bean<IUseCase>(UseCase);
useCase: IUseCase = Bean(UseCase);
useCase: IUseCase = Bean<IUseCase>(UseCase);
//If type is not specified, class will be used as a type
useCase = Bean(UseCaseClass)
//Equivalent to
useCase = Bean<UseCaseClass>(UseCaseClass)
useCase: UseCaseClass = Bean(UseCaseClass)
useCase: UseCaseClass = Bean<UseCaseClass>(UseCaseClass)
//Bean configuration should be passed as a second argument. Should be an object literal.
useCase: IUseCase = Bean(UseCase, {scope: 'prototype'});
}
```
<h5>Second</h5>
#### Usage
```ts
import { Bean } from 'dependency-injection-cat';
```typescript
//UseCaseDependency
export class UseCaseDependency implements IUseCaseDependency {
//...businessLogic
}
class SuperClass {
//If you don't need to pass specific dependencies in Bean, it will resolve all dependencies automatically
someBeanProperty = Bean<Interface>(ImplementationOfInterface);
//UseCase.ts
export class UseCase implements IUseCase {
constructor(
//Any name can be used
private useCaseDependency: IUseCaseDependency,
) {
}
//With Bean configuration
//First argument in Bean should always be implementation of interface, second is configuration object
//When using this syntax, implementation should be a class
//You should pass Bean type in generic
someBeanProperty = Bean<Interface>(ImplementationOfInterface, { qualifier: 'someCoolImpl' });
//...businessLogic
}
//ApplicationContext.di.ts
import { CatContext, Bean } from 'dependency-injection-cat';
class ApplicationContext extends CatContext<IBeans> {
useCaseDependency = Bean<IUseCaseDependency>(UseCaseDependency);
useCase = Bean<IUseCase>(UseCase);
}
```
<h4 id="bean-configuration-object">Bean configuration object</h4>
### Method Bean
```ts
interface BeanConfiguration {
//By default all beans are singleton, if you will set scope 'prototype' Bean will no longer be a singleton
scope?: 'prototype' | 'singleton';
//Read about Qualifiers and their rules below
qualifier?: string;
Method Beans are more flexible, for example it allows you to pass configuration values to your class constructor.
#### Rules
- Method Bean should always have return type, and it should not be primitive (string, number...). To register Bean with
primitive type you should use type alias.
#### Syntax and Usage
```typescript
class ApplicationContext extends CatContext<IBeans> {
//Bean don't have any dependencies
@Bean
useCase(): IUseCase {
return new UseCase();
}
//Bean have dependencies
dependency: IUseCaseDependency = Bean(UseCaseDependency);
@Bean
useCase(
useCaseDependency: IUseCaseDependency,
): IUseCase {
return new UseCase(useCaseDependency);
}
//Configure Bean
@Bean({scope: 'prototype'})
useCase(): IUseCase {
return new UseCase();
}
}
```
## Qualifier
<h4 id="what-is-it">What is it?</h4>
### Arrow Function Bean
In fact, Qualifier it's just a name of Bean
You can use it, if you have a few different implementations of interface
Arrow Function Beans allows you to write more compact code, they work same as "Method Beans"
<h4 id="qualifier-rules">Rules</h4>
#### Rules
<!-- toc -->
- Qualifier should be a string
- Qualifier should not be empty string
- Qualifier should not be dynamically calculated (no template strings, or references to constants/object properties)
<!-- tocstop -->
- Same as for "Method Beans"
<h4 id="qualifier-syntax">Syntax</h4>
#### Syntax and Usage
```ts
import { Bean, Qualifier } from 'dependency-injection-cat';
```typescript
class ApplicationContext extends CatContext<IBeans> {
//Bean don't have any dependencies
@Bean useCase = (): IUseCase => new UseCase();
//OR with body
@Bean useCase = (): IUseCase => {
return new UseCase();
}
class SuperClass {
//Correct example
@Bean
someMethod(
@Qualifier('someQualifier') dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
}
//Bean have dependencies
dependency: IUseCaseDependency = Bean(UseCaseDependency);
@Bean useCase = (useCaseDependency: IUseCaseDependency): IUseCase => new UseCase(useCaseDependency);
//OR with body
@Bean useCase = (useCaseDependency: IUseCaseDependency): IUseCase => {
return new UseCase(useCaseDependency);
}
//Configure Bean
@Bean({scope: 'prototype'}) useCase = (): IUseCase => new UseCase();
}
```
```ts
//Wrong examples
import { Bean, Qualifier } from 'dependency-injection-cat';
const superQualifierName = 'superQualifierNameValue';
### Expression Bean
class SuperClass {
@Bean
someMethod(
@Qualifier(superQualifierName) dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
Expression Beans allows you to define plain value/instance as a Bean, instead of using "Method Beans"
Expression Beans can not have any dependencies, but can be a dependency
#### Rules
- Expression Bean should always have a type, and it should not be primitive (string, number...). To register Bean with
primitive type you should use type alias.
#### Syntax and Usage
```typescript
class ApplicationContext extends CatContext<IBeans> {
@Bean useCase: IUseCase = new UseCase();
@Bean importedInstanceOfUseCase: IUseCase = importedInstanceOfUseCase;
//Configure Bean
@Bean({scope: 'prototype'}) useCase: IUseCase = new UseCase();
}
```
## Context Lifecycle
Context have its own lifecycle, when you're initializing context with `container.initContext` or `container.getOrInitContext`
and when you're clearing context with `container.clearContext`
If you need to make some subscriptions/unsubscription/side effects - you can use **@PostConstruct** and **@BeforeDestruct** decorators
One method can be decorated with **@PostConstruct** and **@BeforeDestruct** at the same time
### PostConstruct, BeforeDestruct
PostConstruct decorator allows you to call method right after context initialization (after calling `container.initContext`), it can be used to make some subscriptions, etc...
BeforeDestruct decorator allows you to call method right before context clearing (after calling `container.clearContext`), it can be used to make some unsubscription, etc...
You can add arguments to annotated method, beans will be injected instead of arguments
You can have several Lifecycle methods, they will be called in order of declaring
#### Syntax
```typescript
import { Bean, PostConstruct, BeforeDestruct, CatContext } from 'dependency-injection-cat';
class ApplicationContext extends CatContext<IBeans> {
@PostConstruct /* OR / AND */ @BeforeDestruct
subscribeToEvents(subscriber: ISubscriber): void {
subscriber.subscribe();
}
@Bean
someMethod(
@Qualifier('') dependency1: InterfaceOfDependency1,
): Interface {
return new ImplementationOfInterface();
}
//OR
@PostConstruct /* OR / AND */ @BeforeDestruct
subscribeToEvents = (subscriber: ISubscriber): void => subscriber.subscribe();
subscriber: ISubscriber = Bean(Subscriber);
}
```
### Container
Container has only one method "get"
### Qualifier
<h4 id="container-rules">Rules</h4>
Qualifier needed, when you have 2 or more **Beans** in the **Context** with same type. By default, the qualifier is the
name of the parameter in the class constructor, or in the **Method Bean**`
Also, you can use qualifier, when injecting **Beans** from **GlobalCatContext**
<!-- toc -->
- You should pass type as generic in get method of Container
<!-- tocstop -->
#### Rules
<h4 id="container-syntax">Syntax</h4>
- @Qualifier decorator can be used only in **Method Bean**
- Argument passed to Qualifier should be a string literal and should not be an empty string
- Qualifier should be a name of Bean (class property name, or method name) defined in current **Context**
```ts
//Any TypeScript file in project
import { container } from 'dependency-injection-cat';
#### Syntax
//Without Qualifier
const someBean = container.get<Interface>();
```typescript
//Implicit declaration of the qualifier
class ApplicationContext extends CatContext<IBeans> {
httpRequester: IRequester = Bean(HttpRequester);
graphQLRequester: IRequester = Bean(GraphQLRequester);
//With Qualifier
const someBean = container.get<Interface>('someQualifier');
@Bean
useCase(
graphQLRequester: IRequester, //Will be injected implementation "GraphQLRequester"
): IUseCase {
return new UseCase(graphQLRequester);
}
}
```
## Author
* [**Artem Kornev**](https://github.com/artem1458)
```typescript
//When Bean placed in current context
class ApplicationContext extends CatContext<IBeans> {
httpRequester: IRequester = Bean(HttpRequester);
graphQLRequester: IRequester = Bean(GraphQLRequester);
## License
This project is under the MIT License
@Bean
useCase(
@Qualifier('graphQLRequester') requester: IRequester,
): IUseCase {
return new UseCase(requester);
}
}
```
```typescript
//When Bean placed in Global context
//GlobalApplicationContext.di.ts
class GlobalApplicationContext extends GlobalCatContext {
graphQLRequester: IRequester = Bean(GraphQLRequester);
httpRequester: IRequester = Bean(HttpRequester);
}
//ApplicationContext.di.ts
class ApplicationContext extends CatContext<IBeans> {
@Bean
useCase(
@Qualifier('graphQLRequester') requester: IRequester,
): IUseCase {
return new UseCase(requester);
}
}
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc