Comparing version 2.0.0 to 2.1.0
@@ -1,2 +0,2 @@ | ||
import { ResolverName } from "./DIContainer"; | ||
import { ResolverName } from "./types"; | ||
export declare function definitionNameToString(definitionName: ResolverName): string; |
@@ -1,31 +0,9 @@ | ||
import { DependencyResolver } from "./DependencyResolver"; | ||
export interface ClassOf<C extends Object> { | ||
new (...args: any[]): C; | ||
} | ||
/** | ||
* Dependency injection container interface to expose | ||
*/ | ||
export interface IDIContainer<ContainerResolvers extends INamedResolvers = {}> { | ||
get: <Custom = void, Name extends ResolverName = ResolverName>(dependencyName: Name) => ResolvedType<Custom, Name, ContainerResolvers>; | ||
} | ||
import { DependencyResolver, IDIContainer, ResolvedType, ResolverName } from "./types"; | ||
interface INamedResolvers { | ||
[k: string]: DependencyResolver | any; | ||
} | ||
declare type Resolve<N extends DependencyResolver> = N extends { | ||
resolve(...args: any[]): infer R; | ||
} ? R : never; | ||
export declare type ResolverName = string | { | ||
name: string; | ||
}; | ||
/** | ||
* Defines the type of resolved dependency | ||
* - if Custom type is given - it will be returned | ||
* - if name of Class is provided - instance type will be returned | ||
* - if function is provided - function return type will be returned | ||
*/ | ||
declare type ResolvedType<Custom, Name extends ResolverName, NamedResolvers extends INamedResolvers> = Custom extends void ? Name extends string ? NamedResolvers[Name] extends DependencyResolver ? Resolve<NamedResolvers[Name]> : NamedResolvers[Name] : Name extends ClassOf<any> ? InstanceType<Name> : Name extends (...args: any) => infer FT ? FT : never : Custom; | ||
/** | ||
* Dependency injection container | ||
*/ | ||
export default class DIContainer<ContainerResolvers extends INamedResolvers = {}> implements IDIContainer<ContainerResolvers> { | ||
export default class DIContainer implements IDIContainer { | ||
private resolvers; | ||
@@ -38,3 +16,3 @@ private resolved; | ||
*/ | ||
get<Custom = void, Name extends ResolverName = ResolverName>(dependencyName: Name, parentDeps?: string[]): ResolvedType<Custom, Name, ContainerResolvers>; | ||
get<Custom = void, Name extends ResolverName = string>(dependencyName: Name, parentDeps?: string[]): ResolvedType<Custom, Name>; | ||
/** | ||
@@ -44,3 +22,3 @@ * Adds multiple dependency resolvers to the container | ||
*/ | ||
add<N extends INamedResolvers>(this: DIContainer<ContainerResolvers>, resolvers: N): asserts this is DIContainer<ContainerResolvers & N>; | ||
add(resolvers: INamedResolvers): void; | ||
/** | ||
@@ -53,2 +31,6 @@ * Adds single dependency definition to the container | ||
} | ||
/** | ||
* Resolves given function parameters | ||
*/ | ||
export declare function resolveFunctionParameters(diContainer: IDIContainer, parameters?: Array<DependencyResolver<any> | any>, parentDeps?: string[]): any[]; | ||
export {}; |
@@ -15,2 +15,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.resolveFunctionParameters = void 0; | ||
var AbstractResolver_1 = __importDefault(require("./resolvers/AbstractResolver")); | ||
@@ -73,2 +74,16 @@ var RawValueResolver_1 = __importDefault(require("./resolvers/RawValueResolver")); | ||
exports.default = DIContainer; | ||
/** | ||
* Resolves given function parameters | ||
*/ | ||
function resolveFunctionParameters(diContainer, parameters, parentDeps) { | ||
if (parameters === void 0) { parameters = []; } | ||
if (parentDeps === void 0) { parentDeps = []; } | ||
return parameters.map(function (parameter) { | ||
if (parameter instanceof AbstractResolver_1.default) { | ||
return parameter.resolve(diContainer, parentDeps); | ||
} | ||
return parameter; | ||
}); | ||
} | ||
exports.resolveFunctionParameters = resolveFunctionParameters; | ||
//# sourceMappingURL=DIContainer.js.map |
@@ -1,4 +0,5 @@ | ||
import DIContainer, { IDIContainer } from "./DIContainer"; | ||
import { diObject as object, diValue as value, diUse as use, diFactory as factory } from "./resolversShorthands"; | ||
import DIContainer from "./DIContainer"; | ||
import { diObject as object, diValue as value, diUse as use, diFactory as factory, diFunc as func } from "./resolversShorthands"; | ||
import { IDIContainer } from "./types"; | ||
export default DIContainer; | ||
export { object, value, use, factory, IDIContainer }; | ||
export { object, value, use, func, factory, IDIContainer }; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.factory = exports.use = exports.value = exports.object = void 0; | ||
exports.factory = exports.func = exports.use = exports.value = exports.object = void 0; | ||
var DIContainer_1 = __importDefault(require("./DIContainer")); | ||
@@ -14,3 +14,4 @@ var resolversShorthands_1 = require("./resolversShorthands"); | ||
Object.defineProperty(exports, "factory", { enumerable: true, get: function () { return resolversShorthands_1.diFactory; } }); | ||
Object.defineProperty(exports, "func", { enumerable: true, get: function () { return resolversShorthands_1.diFunc; } }); | ||
exports.default = DIContainer_1.default; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "rsdi", | ||
"version": "2.0.0", | ||
"description": "Simple dependency injection container for JavaScript/TypeScript", | ||
"scripts": { | ||
"test": "jest", | ||
"format": "prettier 'src/**/*.ts' --write", | ||
"build": "tsc", | ||
"build-prod": "rm -Rf ./dist/* && NODE_ENV=production tsc --build tsconfig.json && cp README.md package.json dist/" | ||
}, | ||
"keywords": [ | ||
"dependency injection", | ||
"dependency", | ||
"injection", | ||
"ioc", | ||
"container", | ||
"javascript", | ||
"typescript" | ||
], | ||
"homepage": "https://github.com/radzserg/rsdi", | ||
"author": "Sergey Radzishevskii <radzserg@gmail.com>", | ||
"license": "ISC", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
"name": "rsdi", | ||
"version": "2.1.0", | ||
"description": "Simple dependency injection container for JavaScript/TypeScript", | ||
"scripts": { | ||
"test": "jest", | ||
"format": "prettier 'src/**/*.ts' --write", | ||
"build": "tsc", | ||
"build-prod": "rm -Rf ./dist/* && NODE_ENV=production tsc --build tsconfig.json && cp README.md package.json dist/", | ||
"pre-commit": "lint-staged" | ||
}, | ||
"keywords": [ | ||
"dependency injection", | ||
"dependency", | ||
"injection", | ||
"ioc", | ||
"container", | ||
"javascript", | ||
"typescript" | ||
], | ||
"homepage": "https://github.com/radzserg/rsdi", | ||
"author": "Sergey Radzishevskii <radzserg@gmail.com>", | ||
"license": "ISC", | ||
"lint-staged": { | ||
"src/**/*.ts": "prettier --write --ignore-unknown" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^26.0.23", | ||
"husky": "^7.0.2", | ||
"jest": "^27.0.6", | ||
"lint-staged": "^11.2.0", | ||
"prettier": "^2.4.1", | ||
"ts-jest": "^27.0.3", | ||
"typescript": "^4.3.5" | ||
} | ||
}, | ||
"lint-staged": { | ||
"src/**/*.ts": "prettier --write --ignore-unknown" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^26.0.23", | ||
"husky": "^7.0.2", | ||
"jest": "^27.0.6", | ||
"lint-staged": "^11.1.2", | ||
"prettier": "^2.4.1", | ||
"ts-jest": "^27.0.3", | ||
"typescript": "^4.3.5" | ||
} | ||
} |
101
README.md
@@ -36,6 +36,6 @@ # RSDI - Dependency Injection Container | ||
```typescript | ||
import DIContainer, { object, use, factory, IDIContainer } from "rsdi"; | ||
import DIContainer, { object, use, factory, func, IDIContainer } from "rsdi"; | ||
export default function configureDI() { | ||
const container: DIContainer = new DIContainer(); | ||
const container = new DIContainer(); | ||
container.add({ | ||
@@ -47,4 +47,4 @@ ENV: "test", // define raw value | ||
), | ||
knex: knex(), | ||
Logger: factory(loggerFactory), | ||
knex: knex(), // keeps raw value | ||
Logger: func(loggerFactory), // lazy function, will be resolved when it will be needed | ||
UsersRepo: factory((container: IDIContainer) => { | ||
@@ -58,3 +58,3 @@ return UsersRepoFactory(container.get("knex")); | ||
An entry point of your application will include | ||
The entry point of your application will include | ||
@@ -75,4 +75,7 @@ ```typescript | ||
- [Object resolver](#object-resolver) | ||
- [Function resolver](#function-resolver) | ||
- [Factory resolver](#factory-resolver) | ||
- [Advanced Usage](#advanced-usage) | ||
- [Typescript type resolution](#typescript-type-resolution) | ||
- [Dependency declaration](#dependency-declaration) | ||
- [Async factory resolver](#async-factory-resolver) | ||
@@ -112,3 +115,3 @@ | ||
const container: DIContainer = new DIContainer(); | ||
const container = new DIContainer(); | ||
container.add({ | ||
@@ -143,3 +146,3 @@ ENV: "PRODUCTION", | ||
// container | ||
const container: DIContainer = new DIContainer(); | ||
const container = new DIContainer(); | ||
container.add({ | ||
@@ -159,9 +162,32 @@ Storage: object(CookieStorage), // constructor without arguments | ||
### Function resolver | ||
Function resolver allows declaring lazy functions. Function will be called when it's actually needed. | ||
```typescript | ||
function UsersRepoFactory(knex: Knex): UsersRepo { | ||
return { | ||
async findById(id: number) { | ||
await knex("users").where({ id }); | ||
}, | ||
}; | ||
} | ||
const container = new DIContainer(); | ||
container.add({ | ||
DBConnection: knex(/* ... */), | ||
UsersRepoFactory: func(UsersRepoFactory, use("DBConnection")), | ||
}); | ||
const userRepo = container.get(UsersRepoFactory); | ||
``` | ||
### Factory resolver | ||
You can use factory resolver when you need more flexibility during initialisation. `container: IDIContainer` will be | ||
pass as an argument to the factory method. So you can resolve other dependencies inside the factory function. | ||
Factory resolver is similar to a Function resolver. You can use factory resolver when you need more flexibility | ||
during initialization. `container: IDIContainer` will be passed in as an argument to the factory method. So you can | ||
resolve other dependencies inside the factory function. | ||
```typescript | ||
const container: DIContainer = new DIContainer(); | ||
const container = new DIContainer(); | ||
container.add({ | ||
@@ -182,15 +208,13 @@ BrowserHistory: factory(configureHistory), | ||
## Advanced Usage | ||
### Typescript type resolution | ||
`container.get` resolves type based on a configured container values | ||
`container.get` and `use` helper resolve type based on following convention: | ||
```typescript | ||
container.add({ key1: "value1", key2: 123, Foo: new Foo() }); | ||
const s: string = container.get("key1"); // resolved as a given type | ||
const i: number = container.get("key2"); | ||
const f: Foo = container.get("Foo"); | ||
``` | ||
- if given name is class - instance of a class | ||
- if given name is function - return type of function | ||
- if custom generic type is provided - custom type | ||
- otherwise - any | ||
`container.get` and `use` helper resolve type based on a given type name. Convention over configuration. | ||
```typescript | ||
@@ -203,6 +227,8 @@ const container: DIContainer = new DIContainer(); | ||
let bar: Bar = container.get(Bar); // types defined based on a given type Bar | ||
let foo: Foo = container.get(Foo); // you can trick TS compiler rsdi relies on COC rule | ||
let foo: Foo = container.get(Foo); // you can trick TS compiler (it's your responsibility) | ||
let foo2: Foo = container.get<Foo>("Foo"); // custom generic type is provided | ||
let foo3: Foo = container.get("Foo"); // any type | ||
``` | ||
`use` example | ||
Example: `use` defines type for a class constructor. | ||
@@ -221,3 +247,3 @@ ```typescript | ||
`container.get` and `use` helper resolve type based on a given factory return type. | ||
Example: `container.get` resolve type based on a given factory return type. | ||
@@ -236,11 +262,32 @@ ```typescript | ||
`use` example | ||
### Dependency declaration | ||
RSDI resolves dependencies on a given type. It can be string or function. In the simplest case, you can use strings. | ||
```typescript | ||
function customFunction() { | ||
return { b: 123 }; | ||
} | ||
const definition: DependencyResolver<{ b: number }> = use(customFunction); | ||
container.add({ | ||
Foo: new Foo(), | ||
}); | ||
const foo = container.get<Foo>("Foo"); | ||
``` | ||
In order to avoid magic strings you can operate with types. | ||
```typescript | ||
const foo = container.get(Foo); | ||
``` | ||
RSDI uses `Foo.name` behind the scene that equals to "Foo". Remember that this approach will not work for uglified code. | ||
You can also rename the function Foo => Buzz, and forget to rename the declaration. From that perspective you can | ||
declare dependencies this way. | ||
```typescript | ||
container.add({ | ||
[Foo.name]: new Foo(), | ||
[MyFactory.name]: MyFactory(), | ||
}); | ||
const foo = container.get(Foo); | ||
const buzz = container.get(MyFactory); | ||
``` | ||
### Async factory resolver | ||
@@ -247,0 +294,0 @@ |
@@ -1,3 +0,2 @@ | ||
import { IDIContainer } from "../DIContainer"; | ||
import { DependencyResolver } from "../DependencyResolver"; | ||
import { DependencyResolver, IDIContainer } from "../types"; | ||
/** | ||
@@ -4,0 +3,0 @@ * Keep AbstractResolver so we can use `if (dep instanceof AbstractResolver) ` checks |
import AbstractResolver from "./AbstractResolver"; | ||
import { IDIContainer } from "../DIContainer"; | ||
import { IDIContainer } from "../types"; | ||
export declare type Factory = (container: IDIContainer) => any; | ||
@@ -10,3 +10,3 @@ /** | ||
constructor(factory: T); | ||
resolve: (container: IDIContainer, _parentDeps?: string[] | undefined) => ReturnType<T>; | ||
resolve: (container: IDIContainer) => ReturnType<T>; | ||
} |
@@ -30,3 +30,3 @@ "use strict"; | ||
var _this = _super.call(this) || this; | ||
_this.resolve = function (container, _parentDeps) { | ||
_this.resolve = function (container) { | ||
return _this.factory(container); | ||
@@ -33,0 +33,0 @@ }; |
import AbstractResolver from "./AbstractResolver"; | ||
import { ClassOf, IDIContainer } from "../DIContainer"; | ||
import { DependencyResolver } from "../DependencyResolver"; | ||
declare type WrapWithResolver<T extends any[]> = { | ||
[K in keyof T]: T[K] | DependencyResolver<T[K]>; | ||
}; | ||
declare type ParametersWithResolver<T extends (...args: any) => any> = T extends (...args: infer P) => any ? WrapWithResolver<P> : never; | ||
declare type MethodArgs<T extends ClassOf<any>, K extends keyof InstanceType<T>> = ParametersWithResolver<InstanceType<T>[K]>; | ||
import { ClassOf, DependencyResolver, IDIContainer, MethodArgs, WrapWithResolver } from "../types"; | ||
/** | ||
@@ -35,2 +29,1 @@ * ObjectDefinition creates objects from the provided class. | ||
} | ||
export {}; |
@@ -32,2 +32,3 @@ "use strict"; | ||
var errors_1 = require("../errors"); | ||
var DIContainer_1 = require("../DIContainer"); | ||
/** | ||
@@ -46,9 +47,4 @@ * ObjectDefinition creates objects from the provided class. | ||
if (parentDeps === void 0) { parentDeps = []; } | ||
var deps = _this.deps.map(function (dep) { | ||
if (dep instanceof AbstractResolver_1.default) { | ||
return dep.resolve(diContainer, parentDeps); | ||
} | ||
return dep; | ||
}); | ||
var object = new ((_a = _this.constructorFunction).bind.apply(_a, __spreadArray([void 0], deps, false)))(); | ||
var constructorParameters = (0, DIContainer_1.resolveFunctionParameters)(diContainer, _this.deps, parentDeps); | ||
var object = new ((_a = _this.constructorFunction).bind.apply(_a, __spreadArray([void 0], constructorParameters, false)))(); | ||
_this.methods.forEach(function (method) { | ||
@@ -59,8 +55,3 @@ var methodName = method.methodName, args = method.args; | ||
} | ||
var resolvedArgs = args.map(function (arg) { | ||
if (arg instanceof AbstractResolver_1.default) { | ||
return arg.resolve(diContainer); | ||
} | ||
return arg; | ||
}); | ||
var resolvedArgs = (0, DIContainer_1.resolveFunctionParameters)(diContainer, args, parentDeps); | ||
object[methodName].apply(object, resolvedArgs); | ||
@@ -67,0 +58,0 @@ }); |
import AbstractResolver from "./AbstractResolver"; | ||
import { IDIContainer } from "../DIContainer"; | ||
import { IDIContainer } from "../types"; | ||
/** | ||
@@ -4,0 +4,0 @@ * Refers to existing definition. i.e. definition with provided name must exists in DIContainer |
@@ -5,3 +5,4 @@ import ObjectResolver from "./resolvers/ObjectResolver"; | ||
import FactoryResolver, { Factory } from "./resolvers/FactoryResolver"; | ||
import { ClassOf, ResolverName } from "./DIContainer"; | ||
import { ClassOf, ResolverName, WrapWithResolver } from "./types"; | ||
import FunctionResolver from "./resolvers/FunctionResolver"; | ||
/** | ||
@@ -34,2 +35,9 @@ * ObjectDefinition creates objects from the provided class. | ||
export declare function diFactory<T extends Factory>(factory: T): FactoryResolver<T>; | ||
/** | ||
* FunctionResolver - allows to use custom function with specified parameters, where parameters are references to | ||
* existing dependencies | ||
* @param func | ||
* @param parameters | ||
*/ | ||
export declare function diFunc<T extends (...args: any) => any>(func: T, ...parameters: T extends (...args: infer ArgTypes) => any ? WrapWithResolver<ArgTypes> : never): FunctionResolver<T>; | ||
export {}; |
"use strict"; | ||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { | ||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { | ||
if (ar || !(i in from)) { | ||
if (!ar) ar = Array.prototype.slice.call(from, 0, i); | ||
ar[i] = from[i]; | ||
} | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,3 +15,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.diFactory = exports.diUse = exports.diValue = exports.diObject = void 0; | ||
exports.diFunc = exports.diFactory = exports.diUse = exports.diValue = exports.diObject = void 0; | ||
var ObjectResolver_1 = __importDefault(require("./resolvers/ObjectResolver")); | ||
@@ -13,2 +22,3 @@ var RawValueResolver_1 = __importDefault(require("./resolvers/RawValueResolver")); | ||
var DefinitionName_1 = require("./DefinitionName"); | ||
var FunctionResolver_1 = __importDefault(require("./resolvers/FunctionResolver")); | ||
// shorthands for Definition classes | ||
@@ -47,2 +57,16 @@ /** | ||
exports.diFactory = diFactory; | ||
/** | ||
* FunctionResolver - allows to use custom function with specified parameters, where parameters are references to | ||
* existing dependencies | ||
* @param func | ||
* @param parameters | ||
*/ | ||
function diFunc(func) { | ||
var parameters = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
parameters[_i - 1] = arguments[_i]; | ||
} | ||
return new (FunctionResolver_1.default.bind.apply(FunctionResolver_1.default, __spreadArray([void 0, func], parameters, false)))(); | ||
} | ||
exports.diFunc = diFunc; | ||
//# sourceMappingURL=resolversShorthands.js.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49918
38
717
321