-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
696 additions
and
524 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
--- | ||
id: about | ||
title: About | ||
sidebar_label: About | ||
--- | ||
|
||
Async context based Dependency Injection for Node.JS without pain with Dependency Injection Container, dependency registration, and configuration. | ||
|
||
- You can use it at any place of your application without rewrite your applications architecture or other preparations or initializations. | ||
- Each dependency can be class, function, or any another value, and plain JavaScript object too. | ||
- You can override your dependencies for organizing modules architecture, or unit testing without hack standard Node.JS require mechanism. | ||
- You can use TypeScript or JavaScript, with decorators or not. Different syntaxes for one mechanism. You can use the constructor to provide dependencies or not, as you wish. | ||
- You can create isolate context for multiple instances of your application (Dependency Injection scopes) with a different set of dependencies, overrides, and instances. | ||
|
||
<!--DOCUSAURUS_CODE_TABS--> | ||
|
||
<!--TypeScript with decorators--> | ||
|
||
```typescript | ||
import { provide, inject } from "node-provide"; | ||
// ... | ||
|
||
class Db { /* ... */ } | ||
class Server { /* ... */ } | ||
// ... | ||
|
||
// Inject dependencies using @provide decorator and class properties | ||
export default class App { | ||
@provide db: Db; | ||
@provide server: Server; | ||
// ... | ||
start() { | ||
this.db.init(); | ||
// ... | ||
} | ||
} | ||
|
||
// or using @inject decorator and constructor parameters | ||
@inject | ||
export default class App { | ||
constructor( | ||
public db: Db, | ||
public server: Server, | ||
) { /* ... */ } | ||
// ... | ||
} | ||
|
||
// index.ts | ||
new App().start(); // You can create an instance directly as usually class | ||
``` | ||
|
||
<!--JavaScript with decorators--> | ||
|
||
```javascript | ||
import { provide, inject } from "node-provide"; | ||
// ... | ||
|
||
// Using @provide decorator | ||
export default class App { | ||
@provide(Db) db; | ||
@provide(Server) server; | ||
// ... | ||
start() { | ||
this.db.init(); | ||
// ... | ||
} | ||
} | ||
|
||
// or using @inject decorator with inject to constructor | ||
@inject([Db, Server]) | ||
export default class App { | ||
constructor(db, server) { /* ... */ } | ||
// ... | ||
} | ||
|
||
// or using @inject decorator with injecting into `this` | ||
@inject({ | ||
db: Db, | ||
server: Server, | ||
}) | ||
export default class App { | ||
start() { | ||
this.db.init(); | ||
// ... | ||
} | ||
// ... | ||
} | ||
|
||
// index.js | ||
new App().start(); // You can create an instance directly as usually class | ||
``` | ||
|
||
<!--Pure JavaScript--> | ||
|
||
```javascript | ||
const { inject, attach, container } = require("node-provide"); | ||
// ... | ||
|
||
const Db = require("./db"); | ||
const Server = require("./server"); | ||
// ... | ||
|
||
const services = container({ | ||
db: Db, | ||
server: Server, | ||
}); | ||
|
||
// Using in function | ||
module.exports = function() { | ||
return { | ||
start() { | ||
services.db.init(); | ||
// ... | ||
}, | ||
// ... | ||
} | ||
} | ||
|
||
// or using attach to `this` in the constructor | ||
module.exports = class App { | ||
constructor() { | ||
attach(this, { | ||
db: Db, | ||
server: Server, | ||
}); | ||
} | ||
// ... | ||
start() { | ||
this.db.init(); | ||
// ... | ||
} | ||
} | ||
|
||
// or using inject decorator with injecting into the constructor | ||
class App { | ||
constructor(db, server) { /* ... */ } | ||
// ... | ||
} | ||
module.exports = inject([Db, Server])(App); | ||
|
||
// or using inject decorator with injecting into `this` | ||
class App { | ||
start() { | ||
this.db.init(); | ||
// ... | ||
} | ||
// ... | ||
} | ||
module.exports = inject(services)(App); | ||
|
||
// index.js | ||
new App().start(); // You can create an instance directly as usually class | ||
``` | ||
|
||
<!--END_DOCUSAURUS_CODE_TABS--> | ||
|
||
|
||
If you have questions or something else for me or this project, maybe architectures questions, improvement ideas or anything else, please make the issue. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
--- | ||
id: api | ||
title: API Reference | ||
sidebar_label: API Reference | ||
--- | ||
|
||
## resolve | ||
|
||
Returns instance of your dependency, or list of instances for an array of dependencies. Each dependency can be class, function or any value. | ||
- For class. The class will be instantiated once and cached | ||
- For function. The function will be called and result cached | ||
- For any value. Return it value without any changes | ||
|
||
```javascript | ||
const depInstance = resolve(Dep); | ||
const [ dep1, dep2, ... ] = resolve(Dep1, Dep2, ...); | ||
``` | ||
|
||
## container | ||
|
||
Returns a plain object with instantiated values. Each argument can be the object of dependencies or result of previous `container` call. The result will be merged. | ||
|
||
```javascript | ||
const cont1 = container({ dep1: Dep1, dep2: Dep2, ... }, { dep3, Dep3 }, ...); | ||
const cont2 = container({ dep4: Dep4 }, cont1, { dep5: Dep5 }, container({ dep6: Dep6 }, ...), ...); | ||
const { dep1, dep2, dep3, dep4, dep5, dep6, ... } = cont2; | ||
``` | ||
|
||
## inject | ||
|
||
Decorator to provide dependencies into object or class. If it runs without arguments it uses reflect metadata for determine list of dependencies from class constructor parameters. For TypeScript your need enable `experimentalDecorators` and `emitDecoratorMetadata` options in your `tsconfig.json`. | ||
|
||
```typescript | ||
@inject // or @inject() its same | ||
class A { | ||
constructor(public dep1: Dep1, public dep2: Dep2, ...) {} | ||
} | ||
const a = new (A as new () => A); // Important: TypeScript can't understanding that constructor signature was changed after use `inject` decorator | ||
// ... | ||
|
||
// Or if A is dependency too you can use `resolve` to get an instance of it | ||
const a = resolve(A); | ||
``` | ||
|
||
If it runs with an array of dependency it works the same but without reflect metadata. | ||
|
||
```javascript | ||
@inject([Dep1, Dep2, Dep3, ...]) | ||
class { | ||
constructor(dep1, dep2, dep3, ...) {} | ||
} | ||
``` | ||
|
||
Or exists signature of this method same as `container`, but return decorator function with injecting all dependency instances into `prototype` if it's class, or into `this` if its plain object. | ||
|
||
```javascript | ||
const decorator = @inject({ dep1: Dep1 }, container({ dep2, Dep2 }), ...); | ||
const Class = decorator(class { | ||
anyMethodOrConstructor() { | ||
const { dep1, dep2 } = this; | ||
} | ||
}); | ||
const obj = decorator({ | ||
anyMethod() { | ||
const { dep1, dep2 } = this; | ||
} | ||
}); | ||
``` | ||
|
||
## provide | ||
|
||
Property decorator for providing an instance of dependency on the class property had two overridden signatures. One of them without parameter used reflect metadata for taking dependency, next one uses dependency from parameter. | ||
|
||
```typescript | ||
class { | ||
@provide dep1: Dep1; | ||
@provide(Dep2): dep2; | ||
} | ||
``` | ||
|
||
In TypeScript exists the problem that it doesn't understand that property from non initialized has been transformed to getter. You can disable `strictPropertyInitialization` in your `tsconfig.json` or using with an exclamation mark. | ||
|
||
```typescript | ||
class { | ||
@provide dep1!: Dep1; | ||
} | ||
``` | ||
|
||
## attach | ||
|
||
Provide instances of dependencies into the object. Signature of function same as the `container`. | ||
|
||
```javascript | ||
const m = {}; | ||
const m2 = attach(m, { dep1: Dep1, ...}, container({ dep2: Dep2, ...}, ...), ...); | ||
console.log(m === m2); // true | ||
const dep1 = m.dep1; | ||
const dep2 = m.dep2; | ||
``` | ||
|
||
## bind | ||
|
||
Function decorator for provide container as the first parameter in the decorated function. Signature of function same as the `container`. | ||
|
||
```javascript | ||
const decorator = bind({ dep1: Dep1, ...}, container({ dep2: Dep2, ...}), ...); | ||
const fn = decorator((cont, x, y) => { | ||
const dep1 = cont.dep1; | ||
const dep2 = cont.dep2; | ||
// ... | ||
}); | ||
fn(x, y); | ||
``` | ||
|
||
## override | ||
|
||
Override dependency. | ||
|
||
```javascript | ||
override(FromDep, ToDep); | ||
// ... | ||
console.log(resolve(FromDep) === resolve(ToDep)); // true | ||
``` | ||
|
||
## assign | ||
|
||
Define any value as resolved value for any dependency. | ||
|
||
```javascript | ||
assign(Dep, value); | ||
// ... | ||
class A {} | ||
assign(A, 10); | ||
console.log(resolve(A)); // 10 | ||
``` | ||
|
||
## isolate | ||
|
||
Run your app in isolated Dependency Injection scope. All instances cached for this instance application will be isolated from all cached instances in other scopes. All overrides defined here will be inherited for nested isolated scopes but not available for others. The return value can be object, function, or any other value: | ||
- For object. All methods will be proxied and their return values converted to promises of them | ||
- For function. The function will be proxied and return value converted to promise of it. | ||
- For any value. Return it value without any changes | ||
|
||
```javascript | ||
const proxy = await isolate(() => { | ||
const app = new App(); // Run you app here | ||
// ... | ||
return { | ||
methodA() {}, | ||
methodB() {}, | ||
// ... | ||
} | ||
}); | ||
// ... | ||
await proxy.methodA(); | ||
await proxy.methodB(); | ||
``` | ||
|
||
```javascript | ||
await isolate(async () => { | ||
override(Dep1, Dep2); | ||
|
||
await isolate(async () => { | ||
override(Dep2, Dep3); | ||
// ... | ||
console.log(resolve(Dep1) instanceof Dep3); // true | ||
}); | ||
// ... | ||
console.log(resolve(Dep1) instanceof Dep2); // true | ||
}) | ||
``` | ||
|
||
## cleanup | ||
|
||
Clean all cached dependency instances. It's needed for testing. Has no parameters. | ||
|
||
```javascript | ||
// ... | ||
after(cleanup); | ||
// ... | ||
``` | ||
|
||
## reset | ||
|
||
Clean all cached dependency instances and overrides. Has no parameters. | ||
|
||
```javascript | ||
reset() | ||
``` |
Oops, something went wrong.